为什么vector :: operator[]的实现不像map :: operator[]?

5
< p >有没有什么理由让 std :: vector operator [] 只返回引用而不是插入新元素? vector :: operator 的cppreference.com页面说 在这里

< blockquote>

std :: map :: operator [] 不同,此运算符从不在容器中插入新元素。

虽然 map :: operator [] 的页面如下所述

< blockquote>

“返回映射到等于键的密钥的值的引用,如果不存在这样的键,则执行插入。”

为什么不能通过调用 vector :: push_back vector :: insert 来实现 vector :: operator [] ,就像 map :: operator [] 一样调用 insert(std :: make_pair(key,T()))。first-&gt; second;


3
实际上实现std::map::operator[] 的方式已经造成了足够多的混淆。对于访问超出边界的 std::vector来说,UB是可以接受的。 - πάντα ῥεῖ
想象一下,如果我们在some_vector的大小为10时写入了some_vector[1000]会发生什么。嗯,我不知道在中间的990个条目中该想象些什么。 - user2486888
5
@KerrekSB,怎么回事?在映射中没有需要初始化的中间条目。 - Tyler
2
我认为问题的区别在于,为什么std::map::operator[]()即使只是访问一个不存在的键,也会插入一个元素? - scottbb
@scottbb 那正是我的疑问。 - 1337ninja
显示剩余5条评论
3个回答

8

简单来说:因为这没有意义。你希望得到什么?

std::vector<int> a = {1, 2, 3};
a[10] = 4;

要做什么?即使您指定了索引10,也要创建第四个元素?创建第3到10个元素并返回对最后一个元素的引用?两者都不是特别直观。
如果您真的想使用operator[]填充vector值而不是push_back,则可以在向量上调用resize来创建这些元素,然后再对它们进行设置。
编辑:或者,如果您实际上想要有一个关联容器,在这种情况下,除排序外索引也很重要,那么std::map<int, YourData>可能更加合适。

2
值得强调的关键点是:对于map,运算符最多在给定的键上插入一个元素。对于vector,您还必须在其他索引处插入其他元素。 - Kerrek SB
值得一提的是,C++比许多其他语言更注重性能问题(例如Java)。这就是为什么vector::operator[]不执行边界检查的原因;如果您觉得需要,可以手动选择加入它们,但是C++希望在这里没有任何开销。即使在向向量中插入不存在的元素有意义,它也会直接违反性能目标。如果您希望进行边界检查,或者希望自动插入元素,则可以在vector之上构建一个更高级别的类并选择加入。 - GManNickG
@GManNickG 所以基本上,如果我想要插入不存在的元素,那么由于向量是一个序列容器,我被迫使用映射吗? - 1337ninja
1
@1337ninja:不是的。就像我写的那样,你可以使用resizepush_back来操作std::vector。这真的取决于你的用例,而你还没有很好地解释它。 - jplatte

2
一个map和一个vector是完全不同的概念。Map是一种“关联容器”,而vector是一种“序列容器”。阐述它们之间的区别超出了这个答案的范围,尽管在最肤浅的层面上,一个map通常被实现为红黑树,而一个vector则是一个包裹在C风格数组上的复杂封装(元素在内存中连续存储)。
如果你想检查一个元素是否已经存在,你需要调整整个容器的大小。但如果你决定删除该元素呢?你该怎么处理刚刚创建的条目呢?用一个map:
std::map<int, int> m; 
m[1] = 1; 
m.erase(m.begin());

这是一个常量操作。
使用向量时:
std::vector<int> v;
// ... initialize some values between 25 and 100
v[100] = 1;
v.erase(v.begin() + 25, v.end());

这是一种线性操作。与 map 相比,这种操作非常低效。虽然这只是一个人为的例子,但可以想象在其他情况下它可能会爆炸。至少,大多数人都会尽量避免使用 operator[],因为它本身就有成本(维护和代码复杂度)。


+1 是因为你提到了向量和指针是基于两个不同的数据结构。向量本质上是一种高级数组,而映射是基于指针的。如果需要使用迭代器进行搜索,则映射非常有用。如果你知道需要哪个元素在数组中,那么向量非常棒。 - Caperneoignis

0
std::vector的operator[]只返回引用而不是插入新元素,这是否有任何原因呢?
std::vector::operator[]以类似数组的方式实现,因为std::vector是一个序列容器(即类似于数组)。对于整数类型的标准数组,不能超出范围进行访问。同样,访问std::vector::operator[]时,如果索引超出向量的长度,则是不允许的。所以,是的,它没有按照您所要求的那样实现的原因是,在C++中没有其他情况下,数组的行为都像那样。
std::map::operator[]不是一个序列容器。它的语法使其类似于其他语言中的关联数组。在C++(及其前身C)方面,map::operator[]只是语法糖。它是operator[]家族中的“黑羊”,而不是std::vector::operator[]。
关于C++规范的有趣部分是,使用std::map::operator[]访问不存在的键会向映射添加一个元素。因此,
#include <iostream>
#include <map>
int main(void) {
   std::map<char, int> m;
   m['a'] = 1;
   std::cout << "m['a'] == " << m['a'] << ", m.size() == " << m.size() << std::endl;
   std::cout << "m['b'] == " << m['b'] << ", m.size() == " << m.size() << std::endl;
}

结果为:

m['a'] == 1, m.size() == 1
m['b'] == 0, m.size() == 2

另请参见:C++中map[]和map.at之间的区别?

如果键不存在,[map::at]会引发异常,如果元素不存在,find会返回aMap.end(),如果相应的键没有值,则operator[]值初始化一个新值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接