没有operator =,复制构造函数或默认构造函数和运行时参数的对象的数组初始化

时间:2014-02-06 10:11:02

标签: c++ arrays c++11 initialization c++14

声明

我正在尝试分配一个对象数组,这些对象既不是可复制的,也不是可赋值的,也没有默认的构造函数。对象具有在运行时确定的参数。我知道你可以通过使用一个指针数组或巧妙地使用placement new来解决这个问题,但我更感兴趣的是,如果可以用C ++ 11(1y)魔法干净利落的话。 所以,请注意,这纯粹是理论上的兴趣,所以不要试图解决“我的问题”。

代码......

...所以问题是:有没有办法在C ++ 11或C ++ 14中完成以下工作:

class X{
public:
  explicit X(int a){...}
  X(const X&) = delete;
  void operator = (const X&) = delete;
private:
  ...
};

class Y{
public:
  Y(const std::vector<int>& args) {
    x = new X[]{args};
  }
  ~Y(){
    delete [] x;
  }
private:
  X* x;
};

标准

具体而言,我正在寻找符合以下标准的解决方案/构造:

  • X不是可复制的。
  • X不可转让。
  • X没有默认的无参数构造函数(构造具有预期的副作用)。
  • X的构造函数的参数直到运行时才知道。
  • X的所有实例必须在内存中连续布局。
  • 当数组从其基指针中删除时(或者如果使用中间类,当中间对象被破坏时),必须正确地破坏X.这排除了指针数组和naivë使用placement new。

编辑/附录

我忘了提到移动构造函数不可用。在实际情况中,X生成一个工作线程并在最初构造的对象的this的上下文中执行,任何使用移动构造函数的尝试都将破坏正在执行的线程的状态。

3 个答案:

答案 0 :(得分:4)

如果您std::vector至少可以移动,则可以使用emplace_back及其X功能。

class X{
public:
  explicit X(int){}
  X(X&&) = default;
  X(const X&) = delete;
  void operator = (const X&) = delete;
};

int main() {
    std::vector<X> xs;
    xs.emplace_back(0);
    xs.emplace_back(1);
    xs.emplace_back(2);
    xs.emplace_back(3);
}

(如果你声明了一个复制构造函数,即使该声明删除它,编译器也不会自动生成任何特殊的移动成员,因此你需要明确地请求它们)

这基本上归结为“阵列新布局”策略,但所有这些都被抽象为高级概念。

如果无法使用可移动类型,则必须实现类似向量的类,该类预先分配存储并且永远不会重新分配。标准库中没有类似的东西。

答案 1 :(得分:4)

您必须手动跟踪构建的元素,但您可以使用allocator来帮助:

class Y{
public:
  Y(const std::vector<int>& args):
    alloc{},
    n{args.size()},
    x{alloc.allocate(n)}
  {
    auto end = x;
    try {
      for (auto arg: args)
        alloc.construct(end++, arg);
    } catch (...) {
      while (end != x)
        alloc.destroy(--end);
      alloc.deallocate(x, n);
      throw;
    }
  }
  ~Y() {
    for (auto end = std::next(x, n); end != x; --end)
      alloc.destroy(end);
    alloc.deallocate(x, n);
  }
private:
  std::allocator<X> alloc;
  const std::size_t n;
  const X *x;
};

答案 2 :(得分:3)

既不可复制也不可移动,也没有默认构造函数的类不能保存在标准容器中(不符合要求)或可变大小的数组分配(只允许固定数字的参数规范)元素)。

这意味着您需要分配原始内存并使用placement new来构造对象。您可以将它包装在固定空间矢量类中。

template <typename T>
class fixed_capacity_vector {
public:
  using size_type = std::size_t;

  fixed_capacity_vector(size_type capacity)
    : data_(::operator new(capacity * sizeof(T)), size_(), capacity_(capacity)
  {}

  fixed_capacity_vector(const fixed_capacity_vector&) = delete;
  fixed_capacity_vector(fixed_capacity_vector&&) = delete;
  fixed_capacity_vector& operator =(const fixed_capacity_vector&) = delete;
  fixed_capacity_vector& operator =(fixed_capacity_vector&&) = delete;

  ~fixed_capacity_vector() {
    for (size_type i = 0; i < size_; ++i) data_[i].~T();
    ::operator delete(data_);
  }

  template <typename... Args>
  T& emplace_back(Args&&... args) {
    if (size_ == capacity_) throw out_of_range();
    new (data_ + size_) T(std::forward<Args>(args)...);
    ++size_;
    return data_[size_-1];
  }

private:
  T* data_;
  size_type size_;
  size_type capacity_;
};