STL 集上的引用运算符 []
The reference operator [] on STL set
我刚刚熟悉 STL,我不太明白为什么operator []
会给出错误。
int main(){
set< int > s;
for(int i=0; i<=1000; i++) s.insert((i*1777)%123);
for(int i=0; i<s.size(); i++) cout<<s[i]<<endl;
}
然后我尝试了这个并收到另一条错误消息
int main(){
set< int > s;
for(int i=0; i<=1000; i++) s.insert((i*1777)%123);
for(int i=0; i<s.size(); i++) cout<<*(s.begin() + i)<<endl;
}
我理解为什么它没有像 push_back
、pop_back
这样的成员,但我不明白为什么这两种引用方法不起作用(但它们适用于vector
和string
(。我知道这些运算符在库中没有过载,但为什么呢?
经过一些网络搜索,我确实弄清楚了如何引用它
int main(){
set< int > s;
for(int i=0; i<=1000; i++) s.insert((i*1777)%123);
for(set< int >::iterator i=s.begin(); i!=s.end(); i++) cout<<*i<<endl;
}
该标准没有为集合或其迭代器指定这些运算符,因为这些不是访问集合的有效方法。 集合具有双向迭代器。 这意味着,为了移动到迭代序列中的第 n 个元素,您需要迭代其间的每个元素。 因此,例如,如果要为集合的迭代器实现 operator+,在内部,它将是这样的:
iterator operator+(iterator it, size_t n)
{
for (int i=0; i<n; ++i)
++it;
return it;
}
因此,换句话说,这将是一个 O(n( 操作。 如果你像在 for 循环中那样遍历集合,它就会变成一个 O(n^2( for 循环。 如果实现了operator[]
,同样的事情也适用。 正因为如此,没有一个考虑效率的人会想要使用这些运算符,因此它们没有实现。
嗯,这是因为std::set
没有提供下标operator[]
由于集合的性质而完全可以理解的内容。集合[4]应该是什么意思?从数学上讲,这是不正确的。在数学中,set1={1,2,3,4} 和 set2={4,3,2,1} 是相等的,那么如果这些集合中每两个 set1[n] 和 set2[n] 都不同(在std::set
的情况下,元素被排序,所以它会是一样的(?因此std::set
没有下标operator[]
但是您仍然可以循环访问此容器。
int myints1[]= {10,20,30,40,50};
int myints2[]= {50,40,30,20,10};
std::set<int> s1 (myints1,myints1+5);
std::set<int> s2(myints2,myints2+5); // Internally, the elements in a set are
// always sorted following a specific strict
// weak ordering criterion indicated by its
// internal comparison object, so this set
// will be the same as s2
if(s1==s2){
printf("sets: true");
}else printf("sets: false");
std::set<int>::iterator it2=s2.begin();
for(std::set<int>::iterator it1=s1.begin();it1!=s1.end();it1++){
printf("ns1: %d s2: %d",*it1,*it2);
it2++;
}
输出:
套:真
S1:10S2:10
S1:20S2:20
S1:30S2:30
S1:40S2:40
S1:50S2:50
STL 集不会重载下标运算符 []
。您不能像使用其他容器(如 vector
(那样直接使用下标运算符访问 STL 集元素。你可以在这里找到STL集的完整参考:STL集
@BenjaminLindley正确地指出,T& operator[](std::size_t)
对于没有随机访问迭代器的容器没有意义,因为所有随机访问循环的复杂性O(N^2)
(元素上的外部循环是线性的,迭代器上的std::advance
是线性的(。出于这个原因,在序列容器中,只有std::array
、std::vector
和std::deque
提供operator[]
,但std::list
(双向迭代器(和std::forward_list
(前向迭代器(不提供。
有序关联容器(std::set
、std::map
和它们的多表亲(只提供双向迭代器,无序关联容器(std::unordered_set
、std::unorderd_map
和它们的多表亲(至少具有前向迭代器。他们也没有operator[](std::size_t)
作为成员。因此,你需要写std::advance(my_set.begin(), n)
而不是my_set[n]
,这使得这种调用的O(N)
复杂性令人痛苦。
补充说明:类似映射的容器包含键值对,这些容器的关联性质通过另一个operator[]
表示,但不通过偏移量索引,而是使用"关联"键,并且它们具有签名Value& operator[](Key const&)
(以及自 C++11 以来的右值引用重载(。这些运算符O(log N)
std::map
的复杂性,以及摊销O(1)
std::unordered_map
复杂性。例如,这将使这些容器的所有键的循环分别O(N log N)
和O(N)
复杂性。
关联operator[]
版本还具有插入语义:像 my_map[my_key] = my_value;
这样的调用将尝试将my_key, my_value
对插入到映射中,如果这样的元素已经存在,则返回迭代器。请注意,这些关联元素访问也没有const
重载:为此使用 find()
成员函数。
对于std::set
重载operator[](Key const&)
没有任何意义,因为它只会表达键与自身关联的事实,并且插入语义已经通过 insert()
成员函数更直接地表达。
如果 BST 的每个节点都保持其后代的计数,则 BST 的第 i 个元素可以在 O(log(n(( 时间内找到,整个树的大小可以在 O(1( 时间内返回。 每次插入和删除的开销将是 O(log(n((,这已经是 O(log(n(( 时间操作。
- 取消引用运算符不能重载
- 为什么在我的函数类型后使用引用运算符 (&) 允许我修改它返回的值?
- 标准库类型的赋值运算符的引用限定符
- 使用运算符 [] 引用 std::vector 上最后一个元素时出现问题<>
- 移动赋值运算符;尝试引用已删除的函数.我该如何解决这个问题?
- 为什么我的运算符 + 重载尽管是通过引用传递的,但仍调用我的复制构造函数?
- C++编程:运算符重载中的引用如何工作?
- 重载运算符*以获取对另一个类的实例的引用
- 使用 scope 运算符 (::) 引用另一个文件中的类
- 对运算符=使用通用引用,而不是多个重载
- 如何重载下标运算符 [] 以引用 2d STL 数组?
- 未定义的引用和运算符 << vs me
- 为什么我不能在运算符=中使用引用类型?
- C++:对函子重载调用运算符的未定义引用
- 运算符重载C++类中的引用返回
- 如何在C++中使用 new 运算符创建对动态创建的数组的引用?
- 对模板运算符的未定义引用,其定义位于同一头文件中
- 为什么在通过引用返回运算符分配时取消引用'this'指针?
- 如果可能的话,C++总是更喜欢右值引用转换运算符而不是常量左值引用吗?
- 运算符++:引用与值返回和未使用的参数