I'm studying C++ and I'm reading about STL containers,iterators and the operations that can be performed on them.
I know that every container type (or better, the corresponding template of which each type is an instance) defines a companio type that acts like a pointer-like type and it's called iterator. What I understand is that once you get an iterator to a container,performing operations like adding an element may invalidate that iterator,so I tried to test this statement with an example:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec={1,2,3,4,5,6,7,8,9,0};
auto beg=ivec.begin();
auto mid=ivec.begin()+ivec.size()/2;
while (beg != mid) {
if (*beg==2)
ivec.insert(beg,0);
++beg;
}
for (auto i:ivec)
cout<<i<<" ";
}
here,I'm simply contructing a vector of ints, brace initialize it,and performing a condition based operation,inserting an element in the first half of the container.
The code is flawed I think, because I'm initializing two iterator objects beg
and end and then I use them in the while statement as a condition.
BUT, if the code should change the contents of the container (and it sure does) what happens to the iterators?
The code seems to run just fine,it add a 0 in the ivec[1] position and prints the result.
What I thought is that the beg iterator would point to the newly added element and that the mid iterator would have pointed to the element before the formerly pointed to by mid (it's like the iterators point to the same memory locations while the underlying array,"slides" under.. unless it's reallocated that is)
Can someone explain me this behaviour??
When the standard says iterators are invalidated, this does not guarantee that they will be invalid in the sense of preventing your program from working. Using an invalid iterator is undefined behavior which is a huge and important topic in C++. It doesn't mean your program will crash, but it might. Your program might also do something else--the behavior is completely undefined.
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"
I am working on some C++ program right now and cannot resolve the above question. I see in forms this has been asked before, but neither answer was satisfying to me.
So, I am working with containers (e.g. a list), additionally I have an array with iterators to this list. I have been doing the following: Initially, I set all iterators in the array as pointing to a dummy list (DummyList.begin()).
To check, if the iterator was initialized I used to check (it != DummyList.begin()), in case needed I would then set the iterator to some element of the real list.
This seems to work.
However, when trying this for other contains (i.e. boost::circular_buffer), I got invalid iterator errors, and upon googling I found e.g. this comparing iterators from different containers, which says that comparing iterators from different containers produces undefined behavior.
That would be scary. Is this still the case?
If yes, then why did my program work so far?
And how would I do this otherwise then?
Edit:
The code looks something like this:
Initialization:
std::list<int> DummyList;
M = new std::list<int>::iterator[n + 1];
for (int i=0;i<=n;++i) {
M[i] = DummyList.begin();
}
Later than the check:
if (M[i] == DummyList.begin())
....
Yes, iterators can only be compared if they belong to the same container. Otherwise, the result is Undefined. Note that "appearing to work" is a valid form of Undefined Behaviour (and usually the worst one, as it hides errors).
What you can do in your case is use something like boost::optional to only store valid iterators in your array:
std::array<boost::optional<MyList::iterator>>, 10> iterators;
if (!iterators[i]) // iterator is not initialised
iterators[i] = myList.begin(); // store valid iterator
else
{
MyList::iterator it = *iterators[i]; // access valid iterator
iterators[i] = boost::none; // make iterator in array invalid again
}
take a look at the following code:
#include <algorithm>
#include <deque>
#include <iostream>
using namespace std;
int main()
{
deque<int> in {1,2,3};
deque<int> out;
// line in question
move(in.begin(), in.end(), out.begin());
for(auto i : out)
cout << i << endl;
return 0;
}
This will not move anything. Looking at the example here, one must write the line in question like this:
move(in.begin(), in.end(), std::back_inserter(out));
This makes sense in a way, as std::move expects its first two arguments to be InputInterators (which is satisfied here) and the third one to be an OutputIterator (which out.begin() is not).
What does actually happen if the original code is executed and move is passed an iterator that is not an OutputIterator? Why does C++'s type-safety not work here? And why is the construction of an output-iterator delegated to an external function, i.e. why does out.backInserter() not exist?
The original code tries to dereference and increment out.begin(). Since out is empty, that's a past-the-end iterator, and it can't be dereferenced or incremented. Doing so gives undefined behaviour.
std::move expects [...] the third one to be an OutputIterator (which out.begin() is not).
Yes it is. Specifically, it's a mutable random access iterator, which supports all the operations required of an output iterator, and more.
What does actually happen if the original code is executed and move is passed an iterator that is not an OutputIterator?
That would cause a compile error if the iterator didn't support the operations required of an output iterator needed by the function; or undefined behaviour if the operations existed but did something other than that required of an output iterator.
Why does C++'s type-safety not work here?
Because the type is correct. The incorrect runtime state (being a past-the-end iterator, not the start of a sequence with at least as many elements as the input range) can't be detected through the static type system.
why does out.backInserter() not exist?
That would have to be written separately for all sequence containers: both the standard ones, and any others you might define yourself. The generic function only has to be implemented once, in the standard library, to be usable for any container that supports push_back.
Is the following code guaranteed by the standard to work(assuming st is not empty)?
#include <vector>
#include <stack>
int main()
{
extern std::stack<int, std::vector<int> > st;
int* end = &st.top() + 1;
int* begin = end - st.size();
std::vector<int> stack_contents(begin, end);
}
Yes.
std::stack is just a container adapter.
You can see that .top() is actually (ยง23.3.5.3.1)
reference top() { return c.back(); }
Where c is the container, which in this case is a std::vector
Which means that your code is basically translated into:
extern std::vector<int> st;
int* end = &st.back() + 1;
int* begin = end - st.size();
std::vector<int> stack_contents(begin, end);
And as std::vector is guaranteed to be continuous there should be no problem.
However, that does not mean that this is a good idea. If you need to use "hacks" like this it is generally an indicator of bad design. You probably want to use std::vector from the beginning.
Only std::vector is guaranteed by C++03 to have contiguous elements (23.4.1). In C++1x this will be extended to std::string as well (defect #530).
Yes, it's guaranteed. Vectors are guaranteed to use contiguous storage, so your code will work. It's a bit cludgy though - and if someone changes the underlying container type of the stack, your code will continue to compile without errors, yet the runtime behaviour will be broken.
I don't have a reference to the standard to back this up unfortunately, but there aren't many ways in which it could go wrong I guess:
Specifying std::vector<int> as the container type means that the elements must be stored in a std::vector<int>.
st.top() must return a reference to an element in the underlying container (i.e. an element in the std::vector<int>. Since the requirements on the container are that it supports back(), push_back() and pop_back(), we can reasonably assume that top() returns a reference to the last element in the vector.
end therefore points to one past the last element.
start therefore points to the beginning.
Conclusion: Unless the assumption was wrong, it must work.
EDIT: And given the other answer's reference to the standard, the assumption is correct, so it works.
According to this page, std::stack uses a container class to store elements.
I guess what you suggest works only if the containter store its elements in a linear way (std::vector).
As a default, std::stack uses a std::deque which, as far as I know, doesn't meet this requirement. But If you specify a std::vector as a container class, I can't see a reason why it shoudln't work.
Edit: initial statement redacted, the standard actually does provide a full definition for the stack adaptor, nothing left to implentors. see top answer.
You want a container that has a push and pop method and allows you to inspect elements anywhere in the container and uses a std::vector for storage. There is such a container in the standard template library
it is called std::vector.
Use std::stack only for bondage purposes
Is there any way to check if an iterator (whether it is from a vector, a list, a deque...) is (still) dereferenceable, i.e. has not been invalidated?
I have been using try-catch, but is there a more direct way to do this?
Example: (which doesn't work)
list<int> l;
for (i = 1; i<10; i++) {
l.push_back(i * 10);
}
itd = l.begin();
itd++;
if (something) {
l.erase(itd);
}
/* now, in other place.. check if it points to somewhere meaningful */
if (itd != l.end())
{
// blablabla
}
I assume you mean "is an iterator valid," that it hasn't been invalidated due to changes to the container (e.g., inserting/erasing to/from a vector). In that case, no, you cannot determine if an iterator is (safely) dereferencable.
As jdehaan said, if the iterator wasn't invalidated and points into a container, you can check by comparing it to container.end().
Note, however, that if the iterator is singular -- because it wasn't initialized or it became invalid after a mutating operation on the container (vector's iterators are invalidated when you increase the vector's capacity, for example) -- the only operation that you are allowed to perform on it is assignment. In other words, you can't check whether an iterator is singular or not.
std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not
Non-portable answer: Yes - in Visual Studio
Visual Studio's STL iterators have a "debugging" mode which do exactly this. You wouldn't want to enable this in ship builds (there is overhead) but useful in checked builds.
Read about it on VC10 here (this system can and in fact does change every release, so find the docs specific to your version).
Edit Also, I should add: debug iterators in visual studio are designed to immediately explode when you use them (instead undefined behavior); not to allow "querying" of their state.
Usually you test it by checking if it is different from the end(), like
if (it != container.end())
{
// then dereference
}
Moreover using exception handling for replacing logic is bad in terms of design and performance. Your question is very good and it is definitively worth a replacement in your code. Exception handling like the names says shall only be used for rare unexpected issues.
Is there any way to check if a iterator (whether it is from a vector, a list, a deque...) is (still) dereferencable, i.e has not been invalidated ?
No, there isn't. Instead you need to control access to the container while your iterator exists, for example:
Your thread should not modify the container (invalidating the iterator) while it is still using an instantiated iterator for that container
If there's a risk that other threads might modify the container while your thread is iterating, then in order to make this scenario thread-safe your thread must acquire some kind of lock on the container (so that it prevents other threads from modifying the container while it's using an iterator)
Work-arounds like catching an exception won't work.
This is a specific instance of the more general problem, "can I test/detect whether a pointer is valid?", the answer to which is typically "no, you can't test for it: instead you have to manage all memory allocations and deletions in order to know whether any given pointer is still valid".
Trying and catching is not safe, you will not, or at least seldom throw if your iterator is "out of bounds".
what alemjerus say, an iterator can always be dereferenced. No matter what uglyness lies beneath. It is quite possible to iterate into other areas of memory and write to other areas that might keep other objects. I have been looking at code, watching variables change for no particular reason. That is a bug that is really hard to detect.
Also it is wise to remember that inserting and removing elements might potentially invalidate all references, pointers and iterators.
My best advice would be to keep you iterators under control, and always keep an "end" iterator at hand to be able to test if you are at the "end of the line" so to speak.
In some of the STL containers, the current iterator becomes invalid when you erase the current value of the iterator. This happens because the erase operation changes the internal memory structure of the container and increment operator on existing iterator points to an undefined locations.
When you do the following, iterator is incementented before it is passed to erase function.
if (something) l.erase(itd++);
Is there any way to check if an iterator is dereferencable
Yes, with gcc debugging containers available as GNU extensions. For std::list you can use __gnu_debug::list instead. The following code will abort as soon as invalid iterator is attempted to be used. As debugging containers impose extra overhead they are intended only when debugging.
#include <debug/list>
int main() {
__gnu_debug::list<int> l;
for (int i = 1; i < 10; i++) {
l.push_back(i * 10);
}
auto itd = l.begin();
itd++;
l.erase(itd);
/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end()) {
// blablabla
}
}
$ ./a.out
/usr/include/c++/7/debug/safe_iterator.h:552:
Error: attempt to compare a singular iterator to a past-the-end iterator.
Objects involved in the operation:
iterator "lhs" # 0x0x7ffda4c57fc0 {
type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
state = singular;
references sequence with type 'std::__debug::list<int, std::allocator<int> >' # 0x0x7ffda4c57ff0
}
iterator "rhs" # 0x0x7ffda4c580c0 {
type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
state = past-the-end;
references sequence with type 'std::__debug::list<int, std::allocator<int> >' # 0x0x7ffda4c57ff0
}
Aborted (core dumped)
The type of the parameters of the erase function of any std container (as you have listed in your question, i.e. whether it is from a vector, a list, a deque...) is always iterator of this container only.
This function uses the first given iterator to exclude from the container the element that this iterator points at and even those that follow. Some containers erase only one element for one iterator, and some other containers erase all elements followed by one iterator (including the element pointed by this iterator) to the end of the container. If the erase function receives two iterators, then the two elements, pointed by each iterator, are erased from the container and all the rest between them are erased from the container as well, but the point is that every iterator that is passed to the erase function of any std container becomes invalid! Also:
Each iterator that was pointing at some element that has been erased from the container becomes invalid, but it doesn't pass the end of the container!
This means that an iterator that was pointing at some element that has been erased from the container cannot be compared to container.end().
This iterator is invalid, and so it is not dereferencable, i.e. you cannot use neither the * nor -> operators, it is also not incrementable, i.e. you cannot use the ++ operator, and it is also not decrementable, i.e. you cannot use the -- operator.
It is also not comparable!!! I.E. you cannot even use neither == nor != operators
Actually you cannot use any operator that is declared and defined in the std iterator.
You cannot do anything with this iterator, like null pointer.
Doing something with an invalid iterator immediately stops the program and even causes the program to crash and an assertion dialog window appears. There is no way to continue program no matter what options you choose, what buttons you click. You just can terminate the program and the process by clicking the Abort button.
You don't do anything else with an invalid iterator, unless you can either set it to the begin of the container, or just ignore it.
But before you decide what to do with an iterator, first you must know if this iterator is either invalid or not, if you call the erase function of the container you are using.
I have made by myself a function that checks, tests, knows and returns true whether a given iterator is either invalid or not. You can use the memcpy function to get the state of any object, item, structure, class and etc, and of course we always use the memset function at first to either clear or empty a new buffer, structure, class or any object or item:
bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary.
{
byte buffer[sizeof(i)];
memset(buffer, 0, sizeof(i));
memcpy(buffer, &i, sizeof(i));
return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators".
}
I have already tested this function before I posted it there and found that this function is working for me.
I very hope that I have fully answered your question and also helped you very much!
There is a way, but is ugly... you can use the std::distance function
#include <algorithms>
using namespace std
auto distance_to_iter = distance(container.begin(), your_iter);
auto distance_to_end = distance(container.begin(),container.end());
bool is_your_iter_still_valid = distance_to_iter != distance_to_end;
use erase with increment :
if (something) l.erase(itd++);
so you can test the validity of the iterator.
if (iterator != container.end()) {
iterator is dereferencable !
}
If your iterator doesnt equal container.end(), and is not dereferencable, youre doing something wrong.