The C++11 standard has changed the signature of the erase() methods of standard containers: they now accept const_iterators instead of iterators. The rationale is explained in this document:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2350.pdf
Now, if one implements std::vector<T> in a straightforward way, it is possible to use directly const T * and T * as const and mutable iterator types respectively. So in the erase() method we might have code like this:
iterator erase(const_iterator it)
{
...
for (; it != end() - 1; ++it) {
// Destroy the current element.
it->~T();
// Move-init the current iterator content with the next element.
::new (static_cast<void *>(it)) T(std::move(*(it + 1))); // FAIL
}
...
}
The problem now is that, since it is a const pointer, the static cast will fail.
Is it legal in this case to cast away the constness from it? Note that it never points to a const object (the objects stored in the vector are never const), and that the calling method (erase()) is not const as well.
EDIT: thanks for all the replies. I wanted to respond here to some comments below.
This code comes from a custom vector class (with a similar interface to std::vector) that implements the small buffer optimisation on top of an unrestricted union. The iterators are naked pointers because they need to be the same type both when the vector is using static storage and when it is using dynamic storage, and this seems the most natural way of achieving such result.
The cast to void * is just a matter of habit and consistency within the codebase when interacting with unitialised storage.
Since erase is non-const, yes, you can safely cast away const on the element. However, note that this is not necessary, since a non-const iterator can be obtained from the const iterator:
iterator non_const = begin() + (it - begin());
And that can be used to iterate over the vector.
Related
Simple theoretical question: would a simple pointer be a valid iterator type for std::vector?
For other containers (e.g. list, map), that would not be possible, but for std::vector the held data is guaranteed to be contiguous, so I see no reason why not.
As far as I know, some implementations (e.g. Visual Studio) do some safe checks on debug build. But that is in UB territory, so for well defined behavior I think there is no difference.
Apart for some checks ("modifying" undefined behavior), are there any advantages of using a class instead of a simple pointer for vector iterators?
would a simple pointer be a valid iterator type for std::vector?
Yes. And also for std::basic_string and std::array.
are there any advantages of using a class instead of a simple pointer for vector iterators?
It offers some additional type safety, so that logic errors like the following don't compile:
std::vector<int> v;
int i=0;
int* p = &i;
v.insert(p, 1); // oops, not an iterator!
delete v.begin(); // oops!
std::string s;
std::vector<char> v;
// compiles if string and vector both use pointers for iterators:
v.insert(s.begin(), '?');
std::array<char, 2> a;
// compiles if array and vector both use pointers for iterators:
v.erase(a.begin());
Yes, it can be T*, but that has the slightly annoying property that the ADL-associated namespace of std::vector<int>::iterator is not std:: ! So swap(iter1, iter2) may fail to find std::swap.
A food for thought - an iterator class can also be implemented by the terms of indexes instead of pointers
of course, when a vector reallocates, all the pointers , references and iterators become invalidated.
but at least for iterators, that doesn't have to be the case always, if the iterator holds an index + pointer to the vector, you can create non-reallocation-invalidated iterators that simply returns (*m_vector)[m_index]. the iterator is invalid when the vector dies out, or the index is invalid. in other words, the iterator is invalid only if the term vec[i] is invalid, regardless of reallocations.
this is a strictly non standard implementation of a vector iterator, but non the less an advantage to class based iterator rather than raw pointer.
also, UB doesn't state what should happen when an invalid iterator is being dereferenced. throwing an exception or logging an error fall under UB.
that means that an iterator which does bound checking is significantly slower, but for some cases where speed is not important, "safe but slow iterator" may have more advantages than "unsafe but fast iterator"
As of GCC 4.9.2, it's now possible to compile C++11 code that inserts or erases container elements via a const_iterator.
I can see how it makes sense for insert to accept a const_iterator, but I'm struggling to understand why it makes sense to allow erasure via a const_iterator.
This issue has been discussed previously, but I haven't seen an explanation of the rationale behind the change in behaviour.
The best answer I can think of is that the purpose of the change was to make the behaviour of const_iterator analogous to that of a const T*.
Clearly a major reason for allowing delete with const T* is to enable declarations such as:
const T* x = new T;
....
delete x;
However, it also permits the following less desirable behaviour:
int** x = new int*[2];
x[0] = new int[2];
const int* y = x[0];
delete[] y; // deletes x[0] via a pointer-to-const
and I'm struggling to see why it's a good thing for const_iterator to emulate this behaviour.
erase and insert are non-const member functions of the collection. Non-const member functions are the right way to expose mutating operations.
The constness of the arguments are irrelevant; they aren't being used to modify anything. The collection can be modified because the collection is non-const (held in the hidden this argument).
Compare:
template <typename T>
std::vector<T>::iterator std::vector<T>::erase( std::vector<T>::const_iterator pos );
^^^^^ ok
to a similar overload that is NOT allowed
template <typename T>
std::vector<T>::iterator std::vector<T>::erase( std::vector<T>::iterator pos ) const;
wrong ^^^^^
The first question here is whether constness of an iterator is supposed to imply constnes of the entire container or just constness of the elements being iterated over.
Apparently, it has been decided that the latter is the right approach. Constness of an iterator does not imply constness of the container, it only implies constness of container's elements.
Since the beginning of times, construction of an object and its symmetrical counterpart - destruction of an object - were considered meta-operations with regards to the object itself. The object has always been supposed to behave as mutable with regard to these operations, even if it was declared as const. This is the reason you can legally modify const objects in their constructors and destructors. This is the reason you can delete an object of type T through a const T * pointer.
Given the resolution referred to by 1, it becomes clear that 2 should be extended to iterators as well.
I am making my own STL-like container - tree with any number of children
template<typename Type>
class Node
{
Type value;
Iterator AddChild(const Type & value);
void Remove(const Iterator & where);
...
};
I decided that iterator's operator* should return value of current node, but what should return operator->? Currently, it returns Node<Type>* and it very useful in such situations
Node<int>::Iterator it = tree.begin();
it->AddChild(4);
But my mentor said me, that operator-> should return Type*. What is STL-like way to access Node's methods? Something like it.Ref().MyMethod() doesn't look good.
Your mentor is right, the return type of operator->() should be Type*.
The big idea behind iterators is that they are just smart pointers to some locations inside the container. You can alter the value stored at that location (by assigning to *it) or access its members, but to do more drastic (i.e. structural) changes to container contents, you have to have direct access to the container itself.
That's why in STL, there are no node's methods. Instead, there're container's methods (and also algorithms) that accept iterators as arguments.
In other words, the STL way of doing what you're trying to do is:
Node<int>::Iterator it = tree.begin();
tree.AddChild(it, 4);
operator-> should return YourTree::value_type* and operator* should return YourTree::value_type&. (Actually a YourTree::pointer and YourTree::reference, but these are normally just aliases for * and & of the value type). Note the consistency. Without it, the standard algorithms will not work.
It is up to you to decide what the value_type is. It could well be Node if you want. This however can be confusing and hard to implement consistently. I would leave it as Type.
The programmer expects it->method to be equivalent to (*it).method, so the operator-> should return pointer to the same thing that operator* returns reference to. Normally that should be the value of the iterator, because that's the expected way to get at the value.
You can expose methods of the node as methods of the pointer, i.e. called as it.method, but it's somewhat confusing and in most cases it would require extra data in the iterator compared to methods of the container taking iterator as argument. That's why STL always uses methods of the container taking iterator. E.g. container.insert(iterator, value) inserts value after iterator.
std::map<std::string, std::string> myMap;
std::map<std::string, std::string>::iterator i = m_myMap.find(some_key_string);
if(i == m_imagesMap.end())
return NULL;
string *p = &i->first;
Is the last line valid?
I want to store this pointer p somewhere else, will it be valid for the whole program life?
But what will happen if I add some more elements to this map (with other unique keys) or remove some other keys, won’t it reallocate this string (key-value pair), so the p will become invalid?
Section 23.1.2#8 (associative container requirements):
The insert members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements.
So yes storing pointers to data members of a map element is guaranteed to be valid, unless you remove that element.
First, maps are guaranteed to be stable; i.e. the iterators are not invalidated by element insertion or deletion (except the element being deleted of course).
However, stability of iterator does not guarantee stability of pointers! Although it usually happens that most implementations use pointers - at least at some level - to implement iterators (which means it is quite safe to assume your solution will work), what you should really store is the iterator itself.
What you could do is create a small object like:
struct StringPtrInMap
{
typedef std::map<string,string>::iterator iterator;
StringPtrInMap(iterator i) : it(i) {}
const string& operator*() const { return it->first; }
const string* operator->() const { return &it->first; }
iterator it;
}
And then store that instead of a string pointer.
If you're not sure which operations will invalidate your iterators, you can look it up pretty easily in the reference. For instance for vector::insert it says:
This effectively increases the vector size, which causes an automatic reallocation of the allocated storage space if, and only if, the new vector size surpases the current vector capacity. Reallocations in vector containers invalidate all previously obtained iterators, references and pointers.
map::insert on the other hand doesn't mention anything of the sort.
As Pierre said, you should store the iterator rather than the pointer, though.
Why are you wanting to do this?
You can't change the value of *p, since it's const std::string. If you did change it, then you might break the invariants of the container by changing the sort order of the elements.
Unless you have other requirements that you haven't given here, then you should just take a copy of the string.
What is the difference between a const_iterator and an iterator and where would you use one over the other?
const_iterators don't allow you to change the values that they point to, regular iterators do.
As with all things in C++, always prefer const, unless there's a good reason to use regular iterators (i.e. you want to use the fact that they're not const to change the pointed-to value).
They should pretty much be self-explanatory. If iterator points to an element of type T, then const_iterator points to an element of type 'const T'.
It's basically equivalent to the pointer types:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
A const iterator always points to the same element, so the iterator itself is const. But the element it points to does not have to be const, so the element it points to can be changed.
A const_iterator is an iterator that points to a const element, so while the iterator itself can be updated (incremented or decremented, for example), the element it points to can not be changed.
Unfortunaty, a lot of the methods for the STL containers takes iterators instead of const_iterators as parameters. So if you have a const_iterator, you can't say "insert an element before the element that this iterator points to" (saying such a thing is not conceptually a const violation, in my opinion). If you want do that anyway, you have to convert it to a non-const iterator using std::advance() or boost::next(). Eg. boost::next(container.begin(), std::distance(container.begin(), the_const_iterator_we_want_to_unconst)). If container is a std::list, then the running time for that call will be O(n).
So the universal rule to add const wherever it is "logical" to do so, is less universal when it comes to STL containers.
However, boost containers take const_iterators (eg. boost::unordered_map::erase()). So when you use boost containers you can be "const agressive". By the way, do anyone know if or when the STL containers will be fixed?
Minimal runnable examples
Non-const iterators allow you to modify what they point to:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Const iterators don't:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
As shown above, v.begin() is const overloaded, and returns either iterator or const_iterator depending on the const-ness of the container variable:
How does begin() know which return type to return (const or non-const)?
how does overloading of const and non-const functions work?
A common case where const_iterator pops up is when this is used inside a const method:
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const makes this const, which makes this->v const.
You can usually forget about it with auto, but if you starting passing those iterators around, you will need to think about them for the method signatures.
Much like const and non-const, you can convert easily from non-const to const, but not the other way around:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
Which one to use: analogous to const int vs int: prefer const iterators whenever you can use them (when you don't need to modify the container with them), to better document your intention of reading without modifying.
Use const_iterator whenever you can, use iterator when you have no other choice.
(as others have said) const_iterator doesn't allow you modify the elements to which it points, this is useful inside of const class methods. It also allows you to express your intent.
ok Let me explain it with very simple example first without using constant iterator
consider we have collection of random integers collection "randomData"
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
As can be seen for writing/editing data inside collection normal iterator is used but for reading purpose constant iterator has been used . If you try using constant iterator in first for loop you will get error . As a thumb rule use constant iterator to read data inside collection .