STL集上的引用运算符[]

时间:2013-04-12 19:07:27

标签: c++ visual-c++ stl reference 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_backpop_back这样的成员,但我不明白为什么这两种引用方法不起作用(但它们的确如此) vectorstring)。我知道这些运算符在库中没有超载,但为什么呢?

经过一些网络搜索,我确实想出了如何引用它

        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;
        }

5 个答案:

答案 0 :(得分:6)

标准没有为集合或其迭代器指定那些运算符,因为这些不是访问集合的有效方法。集合具有双向迭代器。这意味着为了移动到迭代序列中的第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[]已实施,则同样适用。因此,没有一个有效率的人会想要使用这些运算符,因此它们没有实现。

答案 1 :(得分:1)

STL set不会重载下标运算符[]。您不能像使用vector等其他容器一样直接使用下标运算符访问STL集元素。 您可以在此处找到STL集的完整参考:STL set

答案 2 :(得分:1)

嗯,这是因为std::set不提供下标operator[]因为set的性质而完全可以理解的内容。应该是什么意思[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++;
}
  

输出:

     

设置:true

     

s1:10 s2:10

     

s1:20 s2:20

     

s1:30 s2:30

     

s1:40 s2:40

     

s1:50 s2:50

答案 3 :(得分:1)

@BenjaminLindley指出T& operator[](std::size_t)对没有随机访问迭代器的容器没有意义,因为所有随机访问循环都具有O(N^2)复杂度(在外部循环中线性元素,迭代器上std::advance的线性时间。出于这个原因,序列容器,只有std::arraystd::vectorstd::deque提供operator[],但std::list(双向迭代器) )和std::forward_list(前向迭代器)没有。

有序关联容器std::setstd::map及其多个兄弟)仅提供双向迭代器和无序关联容器std::unordered_setstd::unorderd_map和他们的多个表兄弟)至少有前向迭代器。他们也没有operator[](std::size_t)成员。因此,您需要编写std::advance(my_set.begin(), n)而不是my_set[n],这会使此类调用的O(N)复杂性变得非常明显。

正如一个补充说明:类似地图的容器包含键值对,这些容器的关联性质通过另一个operator[]表示,但不是由偏移量表示,而是用“关联”表示密钥和签名Value& operator[](Key const&)(以及自C ++ 11以来的rvalue-reference重载)。对于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()没有任何意义,因为它只表示密钥与自身相关联的事实,并且插入语义已经通过{{更直接地表达了1}}成员函数。

答案 4 :(得分:0)

如果BST的每个节点都保持其后代的计数,则可以在O(log(n))时间内找到BST的第i个元素,并且可以在O中返回整个树的大小( 1次。每次插入和删除时的开销都是O(log(n)),这已经是O(log(n)) - 时间操作。