gcc 4.7.0中std::vector::resize()的奇怪行为

strange behaviour of std::vector::resize() with gcc 4.7.0

本文关键字:vector gcc std resize      更新时间:2023-10-16

我仍然对std::vector::resize()的行为感到困惑。考虑以下代码(另请参阅std::vector<type>的类型要求)

struct A {
  A() : X(0) { std::cerr<<" A::A(); this="<<this<<'n'; }
  A(A const&) { assert(0); }  // is required but doesn't fire in vector::resize
  int X;
};
int main()
{
  std::vector<A> a;
  a.resize(4);        // would not compile without A::A(A const&) or A::A(A&&)
}

如果没有A::A(A const&)A::A(A&&),则带有a.resize(4);的行不会编译。然而,该构造函数从未被调用:assert(0)不会激发!有人能向我解释一下吗?

我的解释是,allocator_traits<>(由std::vector::resize()使用)的模板魔术需要存在这两个构造函数中的任何一个,但实际上从未调用过。但是,如果不调用方法,为什么要要求它存在?

标准的最新修订版(n3376)说:

12-如果CCD_ 8,将sz - size()默认插入的元素追加到序列中
13-要求:T应为MoveInsertableDefaultInsertable应为*this

这意味着MoveInsertable是可能发生的任何重新分配所必需的,而DefaultInsertable是实际追加所必需的。因此,只有当向量已经包含元素并且需要重新分配时,复制或移动构造函数才会启动。

事实上,如果我们写:

std::vector<A> a;
a.resize(1);
assert(!a.empty() && a.capacity() < 4);
a.resize(4);

然后调用A的复制或移动构造函数,并触发断言。

为了resize矢量,如果矢量没有足够的空间容纳新大小所需的元素,则必须将现有元素放置到新分配的内存块中。这是通过复制构建它们来完成的。所以你必须有一个复制构造函数来调整向量的大小。在这种情况下,不存在现有的元素,因此不会调用复制构造函数。但它仍然必须存在。

在您的示例中,当您调用vector::resize()方法时,将调用构造函数而不是复制构造函数。这就是为什么您看不到断言被触发的原因。

至于为什么你需要复制构造函数(和移动构造函数,你还没有定义和声明),是因为模板类型必须是可复制可构造的和可移动可构造的。【集装箱要求概述】/15定义了集装箱类型的要求:

— T is DefaultInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p);
— An element of X is default-inserted if it is initialized by evaluation of the expression allocator_traits<A>::construct(m, p);
where p is the address of the uninitialized storage for the element allocated within X.
— T is CopyInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, v);
— T is MoveInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, rv);
— T is EmplaceConstructible into X from args , for zero or more arguments args, means that the following expression is well-formed: allocator_traits<A>::construct(m, p, args);
— T is Erasable from X means that the following expression is well-formed: allocator_traits<A>::destroy(m, p);