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?
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.