0

I have the following C++ code with C++ STL vector,

#include <iostream>
#include <vector>
using namespace std;

int main ()
{   
    vector <int> v;

    for (int i=0; i<15; i++)
        v.push_back (i);

    cout << v[10] << endl;

    return 0;
}

It normally prints the element that is stored into the 10th index. The output be 10.

But I tried the same thing with C++ STL set also,

#include <iostream>
#include <set>
using namespace std;

int main ()
{
    set <int> myset;

    for (int i=0; i<15; i++)
        myset.insert (i);

    cout << myset[10] << endl;

    return 0;
}

It gives me Compilation error showing the following messages :(

prog.cpp: In function ‘int main()’:

prog.cpp:12:18: error: no match for ‘operator[]’ (operand types are ‘std::set’ and ‘int’) cout << myset[10] << endl;

So, my question is, is there any way to print any element of STL sets alike the STL vectors in C++? if yes, how?

Meanwhile we can use iterators but it can work with the full set, as far I know. :)

Shah Shishir
  • 161
  • 2
  • 4
  • 13
  • 1
    Iterators should do the trick. – Charles May 31 '17 at 15:23
  • 3
    because set doesn't have access by index. – ΦXocę 웃 Пepeúpa ツ May 31 '17 at 15:25
  • 2
    Your example is a toy one, but you should remember that a `std::set` does not store duplicates. What if you rewrote your loop to just `insert(1)` each time -- how would you know that `10` is within bounds? Even using an iterator, you would have been accessing an item out-of-bounds since the set would have contained only 1 item. – PaulMcKenzie May 31 '17 at 15:35
  • @PaulMcKenzie I thought about the numbers from 0 to 15 only. Yeah, I know that std::set does not store the same data. My example is not about trying to inserting the same datas each time. Here I used 10 just for example stufffs. That's it. :) – Shah Shishir May 31 '17 at 16:06
  • 1
    Well my point is that your program has a design flaw if you are relying on `[ ]` to access items in a `std::set`. – PaulMcKenzie May 31 '17 at 16:21
  • That makes sense ! :) – Shah Shishir May 31 '17 at 16:25
  • _"It normally prints the element that is stored into the 10th index."_ You mean, the 11th. – Lightness Races in Orbit Feb 17 '18 at 17:25
  • 1
    By the way, are you sure you really want the "10th element" of the set? I mean, is the order within the set important to you? – einpoklum Feb 23 '18 at 00:51
  • Possible duplicate of [Element at index in a std::set?](https://stackoverflow.com/questions/20477545/element-at-index-in-a-stdset) – xskxzr Feb 23 '18 at 05:53

4 Answers4

12

Yes, it's possible, but not using operator[].

std::set doesn't provide operator[] as it isn't a random-access container. Instead, one must use iterators to access its elements.

auto first = myset.begin(); // get iterator to 1st element
std::advance(first, 9);     // advance by 9
std::cout << *first;        // 10th element

Note that std::set is an ordered container and the elements will not appear in the order you inserted them.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
DeiDei
  • 10,205
  • 6
  • 55
  • 80
  • `s/will not/may not/` And we should emphasise that this is the wrong way to use a set. The differences in intended usage between containers are the very reason that different containers exist. Why have vector under 6 different names? – Lightness Races in Orbit Feb 17 '18 at 17:25
5

You cannot access set elements by index. However you can use std::advance on iterator.

set<int>::iterator it = myset.begin();
std::advance(it, 5); // advanced by five

std::next is also there in C++11,

auto it = std::next(myset.begin(), 5);

The Difference between this two Versions is explained here: What's the difference between std::advance and std::next?

Raynigon
  • 660
  • 1
  • 9
  • 21
Shashwat Kumar
  • 5,159
  • 2
  • 30
  • 66
2

the problem is that sets don't have access by index. but you still can do something like:

set<int>::iterator myIterator = myset.begin();
advance(myIterator , 9);
int theTenth= *myIterator;

which is basically getting an interator and "moving it" forward 9 places...

ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
1

You can't do it in clear C++, but if you use GCC (and you probably do, based on your compilation error), you can create policy based set, which behaves as normal STL set, but supports operation you asked about.

#include <iostream>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> indexed_int_set;

int main ()
{
    indexed_int_set myset;

    for (int i=0; i<15; i++)
        myset.insert (i);

    cout << *myset.find_by_order(10) << endl;

    return 0;
}

In the code above, we define structure called indexed_int_set, which has 2 additional methods: find_by_order(int p) and order_of_key(int k). First one is what you want, it returns an iterator to p-th element. Second one is similar to lower_bound, but returns an index instead of iterator.

bebidek
  • 592
  • 3
  • 8