为什么std::vector需要运算符=
Why std::vector requires operator =
我有一个关于可以存储在vector中的类的问题。可以存储在矢量中的要求是什么?似乎这样的类必须有赋值运算符。但我不确定这是否就是全部。
让我给你举个例子。类A具有const int成员。如果我不写运算符=,它就不会编译。但是在这个例子中,这个操作符什么也不做。此程序正确显示10和20。看起来运算符=是必需的,但在实际中没有使用。
#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();
}
这可能会让您大吃一惊:
v.push_back(A(20));
v.push_back(A(10));
std::sort(begin(v), end(v));
向量本身的某些方面需要可赋值性,尽管我不知道,但这一点我无法通过编译您的代码来判断,因为当我删除operator=()
时,我的编译器不会抱怨)。根据维基百科(引用了'03标准的相关部分),元素必须是CopyConstructible
和Assignable
。
编辑:一天后再回到这一点,当std::vector
需要Assignable
时——无论何时,它都必须四处移动元素,这似乎是非常明显的。例如,如果添加对v.insert()
或v.erase()
的调用,则编译将失败。
如果我不写运算符=,它就不会编译。
这让我很惊讶,所以我查看了一下标准,发现:
您的示例有一个隐式删除的副本构造函数,但如果手头有一个符合C++11标准的库,则仍应进行编译
在您的示例中,唯一对vector
使用的类型施加约束的表达式是push_back
。
具有分配器A
和value_type
T
的序列容器类型X<T,A>
的push_back()
方法要求T
为:
- CopyInsertable如果传递了左值或常量右值引用
- MoveInsertable如果传递了非常值
这意味着它需要一个有效的复制构造函数,或者(在本例中)一个从代码中隐式存在的有效的移动构造函数。因此,在任何具有有效C++11标准库的编译器中,编译都不应该失败。
要求vector
中包含的类型可赋值的操作:
附属条件
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是vector
,则要求T
可赋值的操作为:
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
是可赋值的。
对vector的push_back将使vector在内存中增长,这意味着需要通过赋值运算符将旧对象复制到新对象=因此您需要赋值运算符=。
如果不创建自己的副本构造函数和赋值运算符,编译器将保证它们的可用性。(默认实现的正确性则是另一回事)。在您的示例中,几乎不必重载A::opeartor=()。
这里的问题不是"缺少"运算符=(),而是不能使用默认的运算符,因为您已经声明了一个常量数据成员
const int a_;
默认编译器生成的运算符=()无法为常量成员赋值。所以你必须让自己超负荷:
A & A::opeartor=(const A & in)
{
*const_cast<int*>(&a_) = in.a_; // !!!you are expected to do this.
}
因此,您的A::operator=()版本虽然使代码编译,但不会更改左手操作数的A_值
- 使用运算符 [] 引用 std::vector 上最后一个元素时出现问题<>
- 使用赋值运算符复制 std::vector
- 为什么 std::vector 使用 std::分配器而不是运算符 new 和 delete?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 在设计方面:重载vector类型的类成员的插入运算符
- 重载 std::vector::at 赋值运算符
- 重载运算符时出错<在 sf::Vector 中
- std::vector::emplace() 真的在面对抛出移动构造函数/赋值运算符时提供了强大的异常保证吗?
- 与"运算符[]"不匹配(操作数类型为"std::unique_ptr<std::vector<int> >"和"int")
- 使用运算符重载添加存储在 vector 中的类对象
- SFINAE 为什么我没有检测到 std::vector 的下标运算符?
- 如何在我自己的类 Vector 中使用 Move 构造函数而不是 Move 赋值运算符
- std::vector 运算符 [] 的行为不正确
- 重载运算符= 对于类 std::vector 的结构成员<>
- 是否可以为带有 std:string 和 std::vector 的 std::map 重载<<运算符<int>?
- C++ vector::erase 抱怨过载解析和删除运算符'=='
- 是STD :: Vector,由于其分配运算符移动其成员的位置,因此是一种非常规类型
- 为什么在 std::vector 中使用索引超出范围的运算符 [] 时没有出现异常?
- 运算符= 不适用于 std::vector c++
- 运算符 +(Vector) 表示点 - 但向量使用点,并且在点声明中未声明