Why is iterating over the set and modifying element not allowed here

101 Views Asked by At

In the following code, we cannot iterate over set s1 using non-const reference. Why?

#include <set>

struct st {
  unsigned int f;
  std::set<int> s2;
};

struct comp {
  bool operator()(const st &lhs, const st &rhs) const {
    return lhs.f < rhs.f;
  }
};

int main() {
  std::set<st, comp> s1;
  st x;

  for (st &m : s1) // compiler error here. requires const st&
    m.s2.insert(begin(x.s2), end(x.s2));

  return 0;
}

thanks to @super I found that it is logical for compiler to not let us use non-const reference to std::set members. Now how and where is this policy declared?

1

There are 1 best solutions below

11
Vlad from Moscow On BEST ANSWER

From the C++17 Standard (26.2.6 Associative containers)

5 For set and multiset the value type is the same as the key type. For map and multimap it is equal to pair<const Key, T>.

6 iterator of an associative container is of the bidirectional iterator category. For associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators.

Thus in this range-based for loop

for (st &m : s1)

you may not use a non-constant reference because constant iterators return constant references to objects pointed to by the iterators.

Actually a range-based for loop is defined the following way (the C++17 Standard, 9.5.4 The range-based for statement)

1 The range-based for statement

for ( for-range-declaration : for-range-initializer ) statement

is equivalent to

{
    auto &&__range = for-range-initializer ;
    auto __begin = begin-expr ;
    auto __end = end-expr ;
    for ( ; __begin != __end; ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

So relative to your range-based for loop in this line

for-range-declaration = *__begin;

there is an attempt to assign a constant reference returned by the constant iterator to a non-constant reference.

Pay attention to that you created an empty set.

std::set<st, comp> s1;

So the loop in any case does not make sense.

Consider using a map instead of the set.