2

我仍然对std::vector在 C++11 中与 a 一起使用的类型的要求感到困惑,但这可能是由错误的编译器(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。这是正确的行为还是错误?如果是前者,如何让代码工作?

4

5 回答 5

14

CopyInsertable正如其他人指出的那样,C++11 标准确实需要。然而,这是 C++11 标准中的一个错误。这已在N3376中更正为MoveInsertableDefaultInsertable

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

以下程序使用 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 的问题中正确演示的那样。

于 2012-09-03T16:59:31.783 回答
3

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

从§23.3.6.3

void resize(size_type sz);

……

要求:TCopyInsertable入*this。

于 2012-09-03T16:36:52.983 回答
2

要在向量中使用一个类,它应该有一个复制构造函数/赋值运算符或一个 noexcept 移动构造函数/赋值运算符。GCC 不编译没有这些的示例是完全正确的。

除了向量之外,你将如何做任何事情而不能够复制或移动它包含的内容?

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

于 2012-09-03T16:34:55.007 回答
2

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

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

void resize(size_type sz);

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

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

于 2012-09-03T16:35:33.413 回答
1

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

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

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

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

但是 libstdc++ 不尊重这一点;请参阅(在 C++11 中)std::vector::resize(size_type) 是否适用于默认的可构造 value_type int[4]?

于 2012-09-03T16:37:06.440 回答