【问题标题】:C++ container with non-copyable non-movable element type具有不可复制不可移动元素类型的 C++ 容器
【发布时间】:2017-02-07 19:26:50
【问题描述】:

我需要一个既不能复制也不能移动的元素容器。这些元素不是默认可构造的,但它们的构造函数得到相同的参数。

容器的大小在其生命周期内不会改变。它应该像内置数组一样简单,但它的大小是在运行时调用构造函数时确定的。

有没有一种简单的方法来实现它,而无需使用std::vector<std::unique_ptr<T>> 产生的内存分配和间接开销?

【问题讨论】:

  • 你试过emplace函数吗?不过,我不确定它们与不可移动的效果如何。
  • 您需要一个基于内存分配和放置的解决方案-newing 元素。
  • @Hayt std::vector<T> 不能与不可复制和不可移动的 T 一起编译。 emplace()ing 元素需要向量增长(可能)。增长操作要求元素可以被复制或移动。
  • dequelist 在使用 emplace_back 时都只需要 TEmplaceConstructible。也许检查一下 boost——它没有一个容器来限制它可以拥有的元素数量吗? set::emplace 在 cppreference 上没有列出对 T 的要求...
  • @RalphTandetzky 啊忘记了。你就在那儿。

标签: c++ arrays containers noncopyable


【解决方案1】:

这是一个简单但不完整的解决方案,假设每个元素都是用相同的参数构造的。它使用placement new就地构造元素(另见this SO question):

#include <cstdlib>
#include <utility>
#include <new>

// sample structure, non-copyable, non-moveable, non-default-constructible
struct Foo
{
  Foo() = delete;
  Foo(const Foo&) = delete;
  Foo& operator = (const Foo&) = delete;
  Foo(Foo&&) = delete;
  Foo& operator = (Foo&&) = delete;

  Foo(int a, char b, double c) : m_a(a), m_b(b), m_c(c) { }

  int m_a;
  char m_b;
  double m_c;
};

template <typename T>
struct MyArray
{
  // Array ctor constructs all elements in-place using the
  // provided parameters
  template <typename... Args>
  MyArray(std::size_t sz, Args&&... args)
    : m_sz(sz),
      m_data(static_cast<T*>(malloc(sz * sizeof(T))))
  {
    for (std::size_t i=0; i<m_sz; ++i)
    {
      new (&m_data[i]) T(std::forward<Args>(args)...);
    }
  }

  ~MyArray()
  {
    for (std::size_t i=0; i<m_sz; ++i)
    {
      m_data[i].~T();
    }
    free(m_data);
  }

  std::size_t m_sz;
  T *m_data;
};

int main()
{
  Foo foo(1, '2', 3.0);
  std::size_t s = 5;
  MyArray<Foo> foo_arr(s, 1, '2', 3.0);
}

请注意,缺少一些内容:

  • 如果在MyArray 的构造函数中抛出异常,此基本实现将泄漏内存。
  • 您可能需要一个迭代器实现,begin()/end() 运算符等,以便更方便并获得与标准容器提供的相同的行为。
  • 为了说明起见,我也没有考虑适当的封装。您可能应该将 m_szm_data 设为私有成员。

【讨论】:

  • 只是在此添加一个仅供参考:该技术称为放置新。当我点击刷新并看到它已经发布时,这与我正在使用的技术完全相同。
【解决方案2】:

由于您要管理的元素既不可移动也不可复制,因此容器只能包含指向元素的指针。在不了解更多信息或您的要求的情况下,很难猜测原始指针或std::unique_ptr 是否更合适。

对于固定大小的容器,std::array 可能是合适的。不幸的是,大小必须是编译时表达式。

另一种方法是使用原始数组,并使用placement new 来构建元素。您可以尝试使用向量而不是原始数组,因为向量使用像普通数组一样的连续内存,但我无法想象如何使用它来存储不可复制、不可移动和不可默认构造的对象。

顺便说一句,在 C++ 中编译时大小未知的非默认可构造对象上构建数组并不是那么简单。我能找到的唯一方法是构建一个适当大小的 char 数组,声明一个指向指向数组开头的类的指针,然后使用放置 new 构建元素:

char buf = new char[n * sizeof(X)];
X* x = reinterpret_cast<X*>(buf);
for(int i=-1; i<n i++) {
    new (x + i) X(i);  // or whatever appropriate ctor...
}

【讨论】:

    【解决方案3】:

    我试图做同样的事情,我正在使用一个简单的解决方法。 这个例子有什么问题?

    class test{
      const int a;
    public:
      test(int i): a(i) {} //no default constructor
    };
    
    //trick for static initializer
    template <typename T> class defer_params: public T{
    public:
      defer_params(): T(1) {} //fixed params
    };
    
    //static array
    //test list1[5]; //this doesn't work
    defer_params<test> list2[5];
    

    问候, 加布里埃尔

    【讨论】:

      【解决方案4】:

      “有没有一种简单的方法来实现它,而无需使用 std::vector 产生的内存分配和间接开销>?”

      开销如此之小,你为什么在乎?这几乎可以肯定是过早的优化,您只会让自己在维护方面头疼。

      【讨论】:

        猜你喜欢
        • 2015-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-14
        • 1970-01-01
        • 1970-01-01
        • 2013-04-08
        相关资源
        最近更新 更多