Std::Set Iterator Automatically Const

std::set iterator automatically const

I you are certain you won't touch the variables that are used for the sorting the you could work around this by using a const_cast like this:

    MyClass tmpClass;
std::set<MyClass > setMyClass;
setMyClass.insert(tmpClass);
std::set<MyClass >::iterator iter;
iter=setMyClass.begin();
const MyClass &tmpClass2=*iter;

MyClass &tmpClass3 = const_cast<MyClass&>(tmpClass2);

Alternatively, you could declare the members of the class that you intend to change as mutable.

Why does std::set seem to force the use of a const_iterator?

A set is like a map with no values, only keys. Since those keys are used for a tree that accelerates operations on the set, they cannot change. Thus all elements must be const to keep the constraints of the underlying tree from being broken.

I get const_iterator instead of iterator from std::set

std::set is not just a set of elements, it is a sorted set of unique elements. Elements of std::set are immutable by design, so that you cannot break std::set invariant by modifying its elements. That's the reason why std::set::iterator and std::set::const_iterator are both constant iterators.

Cppreference on std::set reads:

Member type       Definition 

iterator Constant LegacyBidirectionalIterator
const_iterator Constant LegacyBidirectionalIterator

See also this LWG issue:

Keys in an associative container are immutable. ... For associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators.

Rationale: ... if elements were mutable, there would be no compile-time way to detect of a simple user oversight which caused ordering to be modified. There was a report that this had actually happened in practice, and had been painful to diagnose. If users need to modify elements, it is possible to use mutable members or const_cast.

Accessing an std::set via a dereferenced iterator

The elements, which the iterator points to, are constant. You can read it here in the Notes: Because both iterator and const_iterator are constant iterators (and may in fact be the same type), it is not possible to mutate the elements of the container through an iterator returned by any of these member functions.. But, passing a reference to your printset function would violate this property.

Internally, sets allow fast access via index structures. Changing the elements would require to reorder the set (internally).

You can solve the situation by adding a const modifier to the parameter of your printset function:

void printset(const std::set<int>& Set)

How to use 'mutable' correctly so the set iterator won't be const?

The design of std::set ensures that elements of the set "cannot be modified" i.e. they can only be accessed via const references. This is for a good reason: std::set relies on the order of its elements remaining the same thoughout the lifetime to maintain its internal search tree datastructure.

Ways to deal with this are:

  1. declaring setSalary as a const member function. (Not a good idea since setting the salary probably does modify the object in a way that wouldn't be expected of a const function.)
  2. Extract the employee (C++17)
    //employee->setSalary(0);
    //this->employees.erase(employee);
    auto node = employees.extract(employee);
    node.value().setSalary(0);
  3. Choose a different data structure such as a std::unordered_map<int, Employee> mapping from id to Employee or std::vector<Employee>.

However unless the setSalary function has effects other than modifying the Employee object, just remove the object from the set; The object is deleted in the process anyways...

Converting const auto & to iterator

You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.

You should use the Erase-remove idom.

You can use it with remove_if.

It would look something like:

  nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){

//decide if the element should be deleted
return true || false;

}), nodes.end() );

Currently in the technical specifications, is erase_if.

This is a cleaner version of the same behaviour shown above:

std::erase_if(nodes,[](auto it){

//decide if the element should be deleted
return true || false;
});

How to iterate over a specific range of std::set/std::multiset?

You cannot do set.begin()+a, but you can do std::advance(it, a):

#include <iostream>
#include <set>
#include <string>

int main()
{
std::set<std::string> set={ "aaa", "bbb", "ccc", "ddd", "eee", "fff" };
size_t a = 1, b = 4;
auto it = set.begin(), it_end = set.begin();
std::advance(it, a);
std::advance(it_end, b);
for(; it!= it_end; ++it)
std::cout << *it << " ";
}

Unfortunately you cannot do something like auto it = std::advance(set.begin(), a);, because advance gets the iterator by reference and changes it inplace.

https://ideone.com/8nRjgr

Better solution use std::next, thanks to @Evg:

#include <iostream>
#include <set>
#include <string>

int main()
{
std::set<std::string> set={ "aaa", "bbb", "ccc", "ddd", "eee", "fff" };
size_t a = 1, b = 4;
for(auto it = std::next(set.begin(), a), it_end = std::next(it, b-a); it != it_end; ++it)
std::cout << *it << " ";
}

https://ideone.com/6wgpMZ

How to iterate std::set?

You must dereference the iterator in order to retrieve the member of your set.

std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = *it; // Note the "*" here
}

If you have C++11 features, you can use a range-based for loop:

for(auto f : SERVER_IPS) {
// use f here
}

setshared_ptrT::iterator to setshared_ptrT const::const_iterator

For anyone else: After a few days of thinking, this is the solution I came up with.

An Iterator class template to hold the real iterator, not letting anyone touch it.

template<typename It>
struct Iterator {
Iterator(It &&it) : setIt(it) { }

Iterator &operator++() {
++setIt;
return *this;
}

T const &operator*() {
return **setIt;
}

bool operator==(Iterator<It> const &other) {
return setIt == other.setIt;
}

bool operator!=(Iterator<It> const &other) {
return setIt != other.setIt;
}

std::shared_ptr<T const> ptr() {
return *setIt;
}
private:
It setIt;
};

and then simply doing

auto begin() const {
return Iterator<typename std::set<std::shared_ptr<T>>::iterator>(ptrLut.begin());
}

auto end() const {
return Iterator<typename std::set<std::shared_ptr<T>>::iterator>(ptrLut.end());
}


Related Topics



Leave a reply



Submit