std :: vector <type> </type>的类型要求

时间:2012-09-03 16:27:33

标签: c++ constructor c++11 allocation stdvector

我仍然对在C ++ 11中与std::vector使用的类型的要求感到困惑,但这可能是由错误的编译器(gcc 4.7.0)引起的。这段代码:

struct A {
  A() : X(0) { std::cerr<<" A::A(); this="<<this<<'\n'; }
  int X;
};

int main()
{
  std::vector<A> a;
  a.resize(4);
}

工作正常并产生预期的输出,表明默认的ctor(显式给定)被调用(而不是隐式复制ctor)。但是,如果我将删除的副本ctor添加到班级,即

struct A {
  A() : X(0) { std::cerr<<" A::A(); this="<<this<<'\n'; }
  A(A const&) = delete;
  int X;
};

gcc 4.7.0无法编译,但尝试使用已删除的ctor。这是正确的行为还是错误?如果是前者,如何让代码工作?

5 个答案:

答案 0 :(得分:13)

正如其他人所指出的那样,C ++ 11标准确实需要CopyInsertable。但是,这是C ++ 11标准中的一个错误。此后已在N3376MoveInsertableDefaultInsertable中进行了更正。

vector<T, A>::resize(n)成员函数需要MoveInsertableDefaultInsertable。当分配器DefaultConstructible使用默认的MoveConstructible定义时,这些大致会转换为Aconstruct

以下程序使用clang / libc ++编译:

#include <vector>
#include <iostream>

struct A {
  A() : X(0) { std::cerr<<" A::A(); this="<<this<<'\n'; }
  A(A&&) = default;
  int X;
};

int main()
{
  std::vector<A> a;
  a.resize(4);
}

对我而言打印出来:

 A::A(); this=0x7fcd634000e0
 A::A(); this=0x7fcd634000e4
 A::A(); this=0x7fcd634000e8
 A::A(); this=0x7fcd634000ec

如果删除上面的移动构造函数并将其替换为已删除的复制构造函数,则A不再是MoveInsertable / MoveConstructible,因为移动构造会尝试使用已删除的复制构造函数,正如在OP的问题中正确证明的那样。

答案 1 :(得分:3)

在C ++ 11中,要求取决于所执行的操作。对于std::vector<T>::resize()T的要求是向量CopyInsertable

来自§23.3.6.3

  

void resize(size_type sz);

     

...

     

要求:T应为CopyInsertable *此。

答案 2 :(得分:2)

要在向量中使用类,它应该具有复制构造函数/赋值运算符或noexcept移动构造函数/赋值运算符。 GCC完全没有编译你没有任何这些的例子。

除了矢量之外你怎么做才能复制或移动它包含的内容?

第一个示例有效的原因是,由于您没有定义任何副本或移动构造函数或赋值运算符,因此您将获得默认值。在第二个示例中,由于您显式删除了复制构造函数,因此您不会获得任何自动生成的构造函数或赋值运算符。

答案 3 :(得分:2)

On ideone,我看到对默认构造函数的一次调用。但是正在创建四个对象,其他对象必须以某种方式构建。实际上,默认构造原型对象然后复制四次。

C ++ 11标准(第23.3.6.3节)说将插入“值初始化”对象,但也要求该类型是可复制的:

  

void resize(size_type sz);

     
      
  • 效果:如果sz <= size(),相当于erase(begin() + sz, end());。如果size() < sz,请将sz - size()值初始化元素附加到序列中。
  •   
  • 要求:T CopyInsertable*this
  •   

这里没有编译错误;这是你的代码错了。

答案 4 :(得分:1)

void resize(size_type)需要CopyInsertable,这意味着分配器应该能够构造 - 复制类型:

::new((void*)p)A(A());

这意味着需要复制构造函数。您应该可以使用自定义分配器绕过它:

struct Allocator: public std::allocator<A> {
  void construct(A *, const A &) { }
};

然而libstdc ++并不尊重这一点;见Should (in C++11) std::vector::resize(size_type) work for the default constructible value_type int[4]?