容器固定动态尺寸

MvG*_*MvG 30 c++ containers std sequence c++11

是否有一个固定长度序列的标准容器,其长度在运行时确定.我想将参数传递给每个sequence元素的构造函数,并使用该参数初始化const成员(或引用).我还想在O(1)中的给定索引处获得序列元素.在我看来,我的所有要求都无法同时满足.

  • 我知道std::array有固定长度,但必须在编译时知道该长度.
  • std::vector具有动态大小,并允许使用传递构造函数参数emplace.虽然你可以reserve记忆以避免实际的重新分配,但是类型仍然必须是可移动的,理论上允许这样的重新分配,例如防止const成员.
  • 然后有,std::list并且std::forward_list,它不需要可移动类型,但仍然可调整大小并且在随机访问模式下将表现得相当差.我还认为这些列表可能会产生相当大的开销,因为每个列表节点可能会单独分配.
  • 奇怪的是,std::valarray到目前为止,这是我最好的选择,因为它有一个固定的长度,不会自动调整大小.虽然有一种resize方法,但除非您实际调用该方法,否则您的类型不必是可移动的.这里的主要缺点是缺少自定义构造函数参数,因此使用此方法无法初始化const成员.

我错过了一些替代方案吗?有没有办法调整其中一个标准容器,以满足我的所有要求?


编辑:为了让您更准确地了解我正在尝试做什么,请参阅此示例:

class A {
  void foo(unsigned n);
};

class B {
private:
  A* const a;
  const unsigned i;
public:
  B(A* aa) : a(aa), i(0) { }
  B(A* aa, unsigned ii) : a(aa), i(ii) { }
  B(const std::pair<A*, unsigned>& args) : B(args.first, args.second) { }
  B(const B&) = delete;
  B(B&&) = delete;
  B& operator=(const B&) = delete;
  B& operator=(B&&) = delete;
};

void A::foo(unsigned n) {
  // Solution using forward_list should be guaranteed to work
  std::forward_list<B> bs_list;
  for (unsigned i = n; i != 0; --i)
    bs_list.emplace_front(std::make_pair(this, i - 1));

  // Solution by Arne Mertz with single ctor argumen
  const std::vector<A*> ctor_args1(n, this);
  const std::vector<B> bs_vector(ctor_args1.begin(), ctor_args1.end());

  // Solution by Arne Mertz using intermediate creator objects
  std::vector<std::pair<A*, unsigned>> ctor_args2;
  ctor_args2.reserve(n);
  for (unsigned i = 0; i != n; ++i)
    ctor_args2.push_back(std::make_pair(this, i));
  const std::vector<B> bs_vector2(ctor_args2.begin(), ctor_args2.end());
}
Run Code Online (Sandbox Code Playgroud)

Arn*_*rtz 10

从理论上讲,它vector具有您需要的属性.如您所述,如果元素是不可复制的和/或不可分配的,则不支持可能对所包含类型进行赋值的操作,尤其是任何序列修改(empace_back,push_back,insert等).因此,要创建不可复制元素的向量,您必须在向量构造期间构造每个元素.

正如Steve Jessop在他的回答中指出的那样,如果你首先定义向量const,你甚至无法调用这样的修改动作 - 当然元素也保持不变.

如果我理解正确,你只有一系列构造函数参数,而不是真正的对象序列.如果它只是一个参数并且包含的​​类型具有相应的构造函数,那么事情应该很容易:

struct C
{
  const int i_;  
  C(int i) : i_(i) {}
};

int main()
{
  const std::vector<C> theVector { 1, 2, 3, 42 };
}
Run Code Online (Sandbox Code Playgroud)

如果构造函数是显式的,则必须先创建一个列表,或者在initializer-list中显式构造对象:

int main()
{
  auto list = { 1, 2, 3, 4 };
  const std::vector<C> theVector (std::begin(list), std::end(list));
  const std::vector<C> anotherVector { C(1), C(44) };
}
Run Code Online (Sandbox Code Playgroud)

如果每个构造对象不仅仅是一个参数,请考虑一个中间创建者对象:

struct C
{
  const int i_;  
  C(int i, int y) : i_(i+y) {}
};

struct CCreator
{ 
  int i; int y; 
  explicit operator C() { return C(i,y); }
};

int main()
{
  const std::vector<CCreator> ctorArgs = { {1,2}, {3,42} };
  const std::vector<C> theVector { begin(ctorArgs), end(ctorArgs) };
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*sop 5

我认为const std::vector<T>你有要求的属性.它的元素实际上没有定义const,但它提供他们一个const视图.你无法改变大小.你不能调用任何需要T移动的成员函数,所以对于正常使用它们不会被实例化(如果你做了一个extern类声明,那么你就不能这样做).

如果我错了,你确实遇到麻烦,因为T不可动,请尝试const std::deque<T>改为.

困难在于构建更明亮 - 在C++ 11中你可以用初始化列表来完成这个,或者在C++ 03中你可以const vector用非const向量构造一个或者从其他任何你可以获得迭代器的东西.这并不一定意味着T需要可复制,但确实需要一种可以构造它的类型(也许是你为此目的而发明的一种).