8

我有一个关于我们可以存储在向量中的类的问题。可以存储在向量中的要求是什么?似乎这样的类必须有赋值运算符。但我不确定这是否全部。

让我给你举个例子。A 类具有 const int 成员。如果我不写 operator =,它就不会编译。但是在这个例子中,这个操作符什么都不做。该程序正确显示 10 和 20。看起来 operator = 是必需的,但实际上并未使用。

#include <iostream>
#include <vector>

class A {
 public:
  A(int a) : a_(a) {}
  A& operator =(const A& a2) { return *this;} // Without this, compile fails.
  void print() const {
    std::cerr << a_ << std::endl;
  }
 private:
  const int a_;
};

int main(int argc, char** argv) {
  std::vector<A> v;
  v.push_back(A(10));
  v.push_back(A(20));
  for (const A& a : v) a.print();
}
4

4 回答 4

6

这可能会让你大吃一惊:

v.push_back(A(20));
v.push_back(A(10));
std::sort(begin(v), end(v));

vector 本身的某些方面需要可分配性,尽管我不知道是哪一个(我无法通过编译您的代码来判断,因为我的编译器在删除时不会抱怨operator=())。根据Wikipedia(引用 '03 标准的相关部分),元素必须是CopyConstructibleand Assignable

编辑:一天后回到这一点,当std::vector需要时,它似乎很明显Assignable- 任何时候它必须移动元素。例如,添加对v.insert()or的调用,编译将失败。v.erase()

于 2013-07-20T09:09:23.510 回答
3

如果我不写 operator =,它就不会编译。

这让我感到惊讶,所以我查看了标准,我发现:

您的示例有一个隐式删除的复制构造函数,但如果手头有符合标准的 C++11 标准库,则仍应编译。

vector对示例中使用的类型施加约束的唯一表达式是push_back.

具有分配器和push_back()的序列容器类型的方法需要是:X<T,A>Avalue_type TT

  • 如果传递了左值或 const 右值引用,则CopyInsertable
  • 如果传递了非常量右值,则MoveInsertable

这意味着它需要一个有效的复制构造函数或(如在本例中)一个有效的移动构造函数,它将隐含在您的代码中。因此,编译不应该在任何具有有效 C++11 标准库的编译器中失败。

需要类型的操作,包含在 avector中是可分配的:

辅助条件

typdef std::vector<T> X;
X a,b;
X&& rv;
X::value_type t;
X::value_type&& u;
X::size_type n;
X::const_iterator p,q; // p = valid for a, q = valid and dereferencable
initializer_list<T> il;
[i,j) -> valid iterator-range

悲观*操作列表

如果 X 是 a ,则需要T可分配的操作vector是:

Statement              Requirement on T

a = b;                 CopyInsertable, CopyAssignable
a = rv;                MoveInsertable, MoveAssignable
a = il;                CopyAssignable
a.emplace(p, args);    MoveInsertable, MoveAssignable
a.insert(p, t);        CopyAssignable
a.insert(p, u);        MoveAssignable
a.insert(p, n, t);     CopyInsertable, CopyAssignable
a.insert(p, i, j);     EmplaceConstructible[from *i], MoveInsertable, MoveAssignable
a.insert(p, il);       -> a.insert(p, il.begin(), il.end());
a.erase(q);            MoveAssignable
a.erase(q1,q2)         MoveAssignable
a.assign(i,j);         Assignable from *i
a.assign(il);          -> a.assign(il.begin(), il.end());
a.assign(n,t)          CopyAssignable

*= 悲观的意思是可能存在若干要求实际生效的某些条件。如果您使用上面列出的表达式之一,您的类型T可能需要可分配。

于 2013-07-21T01:34:05.430 回答
2

向量上的 push_back 将使向量在内存中增长,这意味着需要通过赋值运算符 = 将旧对象复制到新对象,因此您需要赋值运算符 =。

于 2013-07-20T11:22:50.900 回答
-1

如果您不创建自己的任何一个,则编译器会保证复制构造函数和赋值运算符的可用性。(虽然默认实现的正确性是另一回事)。在您的示例中,您几乎不必重载 A::operator=()。

这里的问题不是“缺少” operator=(),而是不能使用默认的,因为您已经声明了一个 const 数据成员

const int a_;

默认编译器生成的 operator=() 不能为 const 成员赋值。所以你必须超载自己:

A & A::opeartor=(const A & in)
{
    *const_cast<int*>(&a_) = in.a_; // !!!you are expected to do this.
}

因此,您的 A::operator=() 版本什么都不做,虽然使代码编译,但不会更改左侧操作数的 a_ 值,

于 2013-07-20T11:20:20.543 回答