I have a runtime error "map/set iterators incompatible" at line 8.
void Manager::Simulate(Military* military, Shalishut* shalishut,char* args[]){
Simulation* simulation = Simulation::GetInstance();
Time* time = Time::GetInstance();
multimap<int,Task*>::iterator itTasks;
itTasks = simulation->GetTasks().begin();
while(itTasks != simulation->GetTasks().end()){
while (itTasks->second->GetTimeStamp() == time->GetTime()){ /*line 8 - ERROR*/
TaskExecute(itTasks->second,military,shalishut,args);
itTasks++;
}
// Unit take car of vehicles
time->TimeIncrease();
}
}
Simulation is declared as a multimap<int,Task*>. What is the problem?
I'm going to take a wild guess and say that the Simulation::GetTasks() signature looks like this:
multimap<int,Task*> GetTasks() const;
This creates a new multimap (a copy) each time you call it.
When comparing iterators, both of the multimap<int,Task*> iterators must come from the same container; since you're getting a new copy each time you call GetTasks(), you violate this constraint, and this is the source of your error. You also have another problem - the temporary multimap copies are destroyed after the statement they're created in, so your iterators are invalidated instantly.
You have two choices; one is to capture a copy locally and use that copy consistently:
multimap<int,Task*> tasks = simulation->GetTasks();
multimap<int,Task*>::iterator itTasks;
itTasks = tasks.begin();
while(itTasks != tasks.end()){
while (itTasks->second->GetTimeStamp() == time->GetTime()){
TaskExecute(itTasks->second,military,shalishut,args);
itTasks++;
}
// Unit take car of vehicles
time->TimeIncrease();
}
Another is to have GetTasks() return a reference to a persistent multimap, ensuring the same one is used each time:
multimap<int,Task*> &GetTasks();
Or a const reference:
const multimap<int,Task*> &GetTasks() const;
This has the advantage of avoiding the (potentially large) overhead of copying the multimap.
Note that using a const reference requires using const_iterators to step through the multimap. I would recommend defining both const and non-const accessors (C++ will pick the right one based on if the Simulation pointer or reference is const), unless you want to disallow direct modification of the underlying multimap entirely, in which case you can define only the const variant.
Related
I'm working on a school project of boolean minimization, and here I want to delete some elements of a set of my user defined class.
This is where the error occurs:
(dc and PI are both sets of my class Term, passed to this function by reference. std::set<Term>& dc, PI)
for (const auto& n : dc) {
for (const auto& i : n.getMinterm()) {
m[i] = 0;
for (auto &x : PI) {
x.delMinterm(i);
}
}
}
The error message is:
Error (active) E1086 the object has type qualifiers that are not compatible with the member function "Term::delMinterm"
Error C2662 'void Term::delMinterm(int)': cannot convert 'this' pointer from 'const Term' to 'Term &'
This is the content of my class:
class Term {
private:
int group = 0;
int literal = 0;
std::string term;
std::set<int>minterm;
bool isDontCare;
bool merged;
};
Function delMintern(int) just erases the selected element from the set minterm.
Though I didn't use "const auto&" but "auto&", it still shown as a const object.
I've tried taking off the '&' but it just create a local duplicate, however, I want to directly modify the original one.
I also tried something like:
for (auto x : PI) {
PI.erase(x);
x.delMinterm(i);
PI.insert(x);
}
but it caused a "read access violation" error.
You can't modify a reference to x because it is const. It is const because iterating a std::set through loop gives only const values.
See solution with const_cast example code at the end of my answer.
It is known that std::set stores all entries in a sorted tree.
Now imagine if you can modify a variable when iterating a loop, it means that after modification sorted order of std::set might be changed. But std::set should always keep invariant of its tree, hence it can't allow to make any modifications thus gives only const values when iterating.
If you need to really modify set entry then you have to take it from set, delete from set and that add again to set. Even if sorted order is not changed after your modification, still you need to reinsert into set.
But there is a hacky workaround - you can make your method delMinentry as having const modifier. Then all fields that it modifies should be marked as mutable. Mutable fields allow modifications from const methods. And std::set allows to call const methods when iterating.
There is one more workaround - you make delMinterm() as const method, but inside this method do const_cast<Term &>(*this).delMintermNonConst(), in other words from const method you can call non-const method if you do const_cast. Also you can do const cast directly on loop variable if you're sure what you do, in other words if you modify Term in such a way that std::set sorted order is not changed:
for (auto &x : PI) {
const_cast<Term &>(x).delMinterm(i);
}
If delMinterm() results in such modification of a Term after which order of std::set may change then you can't do const_cast in code above. In other words if after delMinterm your operator < may give a different result between terms, then you can't do this const cast and you have to reinsert into set (delete Term and add again).
Also don't forget that after reinserting into set you have to redo set iteration loop again from start, because after change to inner structure you can't keep iterating loop running further, iterators are invalidated.
If set's order changes (hence you can't do const_cast) then you have to re-insert values of set, do this by copying values to vector, modifying them through delMinterm(), copying back to set, like this:
std::vector<Term> scopy(PI.cbegin(), PI.cend());
for (auto & x: scopy)
x.delMinterm(i);
PI = std::set<Term>(scopy.cbegin(), scopy.cend());
The Qt's documentation says the following:
Thanks to implicit sharing, it is very inexpensive for a function to return a container per value. The Qt API contains dozens of functions that return a QList or QStringList per value (e.g., QSplitter::sizes()). If you want to iterate over these using an STL iterator, you should always take a copy of the container and iterate over the copy. For example:
// RIGHT
const QList<int> sizes = splitter->sizes();
QList<int>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)
...
// WRONG
QList<int>::const_iterator i;
for (i = splitter->sizes().begin();
i != splitter->sizes().end(); ++i)
...
What will happen if the 'Wrong' method is applied?
The two calls to splitter->sizes() produce two distinct copies of the container. Since begin() comes from one and end() from the other, they don't form a valid range. The loop would then walk off the end of the first container, into the land of undefined behavior.
Range-based loop would work just fine though: for (int size: splitter->sizes()) { ... }
EDIT: TLDR? Here's a summary:
The requirement is for an essentially infinitely (or arbitrarily) long container. So list sounds like a good idea, because it will fit the objects in whatever memory space is available.
However vectors are much faster/efficient at access, but might not be able to fit in memory if we don't have a long sequential strip.
Vector of pointers was suggested to reduce memory usage, but the problem remains if there are a gigabyte of pointers and I have 4GB of ram, it might just not fit!
Solution: A list of vectors might be the way to go. Each item in the list could be a vector with 1000 pointers to items which we want to be able to access. A class could handle this functionality.
** Original Question:**
As a wise man once said: "With pointers, if it works once, that doesn't guarantee you are doing it correctly."
I have a class:
class A;
And class A is inside a std::list:
std::list<A> list_of_A;
To access items inside it I am using:
std::list<A>::iterator iter = list_of_A.begin();
std::advance(iter, <an_unsigned_int>);
return *iter;
This seems to be working, but is return *iter the correct thing to be doing? I should mention the last 3 lines are inside a function which returns a const A&.
I looked for an answer on stackoverflow, but couldn't find a duplicate of this question, which surprises me.
List > Vector because I will be swapping things in and out of the list.
Yes; you will return a reference inside the list if your function returns A& or A const& and a copy if your function returns A.
However, if you are doing this regularly, why not use a std::vector? They have random access iterators and are almost always more efficient than a std::list, unless the objects are large and you have a large number of them. std::list are very cache-inefficient.
This is good as long as you have not advanced to (or past) end().
const A& stuff(std::list<A>& list_of_A, int index)
{
assert(index <= list_of_A.size()); // Protect against UB
// of advancing past end.
std::list<A>::iterator iter = list_of_A.begin();
std::advance(iter, index);
if (iter == list_of_A.end())
{ throw std::runtime_error("Failed"); // Not allowed to de-reference end()
}
return *iter;
}
I need to modify an object that has already been inserted into a set. This isn't trivial because the iterator in the pair returned from an insertion of a single object is a const iterator and does not allow modifications. So, my plan was that if an insert failed I could copy that object into a temporary variable, erase it from the set, modify it locally and then insert my modified version.
insertResult = mySet.insert(newPep);
if( insertResult.second == false )
modifySet(insertResult.first, newPep);
void modifySet(set<Peptide>::iterator someIter, Peptide::Peptide newPep) {
Peptide tempPep = (*someIter);
someSet.erase(someIter);
// Modify tempPep - this does not modify the key
someSet.insert(tempPep);
}
This works, but I want to make my insert more efficient. I tried making another iterator and setting it equal to someIter in modifySet. Then after deleting someIter I would still have an iterator to that location in the set and I could use that as the insertion location.
void modifySet(set<Peptide>::iterator someIter, Peptide::Peptide newPep) {
Peptide tempPep = (*someIter);
anotherIter = someIter;
someSet.erase(someIter);
// Modify tempPep - this does not modify the key
someSet.insert(anotherIter, tempPep);
}
However, this results in a seg fault. I am hoping that someone can tell me why this insertion fails or suggest another way to modify an object that has already been inserted into a set.
The full source code can be viewed at github.
I agree with Peter that a map is probably a better model of what you are doing, specifically something like map<pep_key, Peptide::Peptide>, would let you do something like:
insertResult = myMap.insert(std::make_pair(newPep.keyField(), newPep));
if( insertResult.second == false )
insertResult.first->second = newPep;
To answer your question, the insert segfaults because erase invalidates an iterator, so inserting with it (or a copy of it) is analogous to dereferencing an invalid pointer. The only way I see to do what you want is with a const_cast
insertResult = mySet.insert(newPep);
if( insertResult.second == false )
const_cast<Peptide::Peptide&>(*(insertResult.first)) = newPep;
the const_cast approach looks like it will work for what you are doing, but is generally a bad idea.
I hope it isn't bad form to answer my own question, but I would like it to be here in case someone else ever has this problem. The answer of why my attempt seg faulted was given my academicRobot, but here is the solution to make this work with a set. While I do appreciate the other answers and plan to learn about maps, this question was about efficiently re-inserting into a set.
void modifySet(set<Peptide>::iterator someIter, Peptide::Peptide newPep) {
if( someIter == someSet.begin() ) {
Peptide tempPep = (*someIter);
someSet.erase(someIter);
// Modify tempPep - this does not modify the key
someSet.insert(tempPep);
}
else {
Peptide tempPep = (*someIter);
anotherIter = someIter;
--anotherIter;
someSet.erase(someIter);
// Modify tempPep - this does not modify the key
someSet.insert(anotherIter, tempPep);
}
}
In my program this change dropped my run time by about 15%, from 32 seconds down to 27 seconds. My larger data set is currently running and I have my fingers crossed that the 15% improvement scales.
std::set::insert returns a pair<iterator, bool> as far as I know. In any case, directly modifying an element in any sort of set is risky. What if your modification causes the item to compare equal to another existing item? What if it changes the item's position in the total order of items in the set? Depending on the implementation, this will cause undefined behaviour.
If the item's key remains the same and only its properties change, then I think what you really want is a map or an unordered_map instead of a set.
As you realized set are a bit messy to deal with because you have no way to indicate which part of the object should be considered for the key and which part you can modify safely.
The usual answer is to use a map or an unordered_map (if you have access to C++0x) and cut your object in two halves: the key and the satellite data.
Beware of the typical answer: std::map<key_type, Peptide>, while it seems easy it means you need to guarantee that the key part of the Peptide object always match the key it's associated with, the compiler won't help.
So you have 2 alternatives:
Cut Peptide in two: Peptide::Key and Peptide::Data, then you can use the map safely.
Don't provide any method to alter the part of Peptide which defines the key, then you can use the typical answer.
Finally, note that there are two ways to insert in a map-like object.
insert: insert but fails if the value already exists
operator[]: insert or update (which requires creating an empty object)
So, a solution would be:
class Peptide
{
public:
Peptide(int const id): mId(id) {}
int GetId() const;
void setWeight(float w);
void setLength(float l);
private:
int const mId;
float mWeight;
float mLength;
};
typedef std::unordered_map<int, Peptide> peptide_map;
Note that in case of update, it means creating a new object (default constructor) and then assigning to it. This is not possible here, because assignment means potentially changing the key part of the object.
std::map will make your life a lot easier and I wouldn't be surprised if it outperforms std::set for this particular case. The storage of the key might seem redundant but can be trivially cheap (ex: pointer to immutable data in Peptide with your own comparison predicate to compare the pointee correctly). With that you don't have to fuss about with the constness of the value associated with a key.
If you can change Peptide's implementation, you can avoid redundancy completely by making Peptide into two separate classes: one for the key part and one for the value associated with the key.
I have a function that reads lines from a log file, converts these lines to a certain class and returns a STL list of instances of this class.
How should I declare this function so that the whole list is NOT copied when attributing it to the caller?
Without loss of generality, assume:
list<Request> requests = log_manipulator.getAsRequestList();
How should I declare getAsRequestList()? Should I return a reference to a list or just return a list?
This is a serious issue because in this particular assignment the lists will contain circa 1.5M elements, and thus a mistake like that can screw up memory usage.
Returning a reference is not advisable, and returning the list object would cause copying. Best would be to change the method's signature to accept and populate a list reference:
list<Request> requests;
log_manipulator.getRequestListByRef(requests);
with void getRequestListByRef(list<Request>&) as the method's signature.
You have two easy options:
A return parameter
void getAsRequestList(list<Request>& requests);
...
list<Request> requests;
log_manipulator.getAsRequestList(requests);
An output iterator
template <class OutputIterator>
void getAsRequestList(OutputIterator dest);
...
list<Request> requests;
log_manipulator.getAsRequestList(
insert_iterator< list<Request> >(requests, requests.begin()) );
See also: How do I return hundreds of values from a C++ function?
You might be able to get away with returning a simple list - search for "Return Value Optimization" for details. Simply, the compiler is allowed to generate code that bypasses the costly copy constructor.
Only after trying this and not being satisfied with the results would I recommend the "populate" versions suggested in the other answers. It's uglier.
Of course, the caveat is that RVO is not guaranteed to happen.
Return a auto_ptr to the list:
auto_ptr<list<Request> > getAsRequestList()
{
auto_ptr<list<Request> > list = new list<Request>();
// populate list
return list;
}
Return a local variable as a reference is a funny thing in C++,
list<Request>& requests = log_manipulator.getAsRequestList();
it is compiler dependent so works in one machine but not the other.
your best bet is to declare your getAsRequestList() this way
void getAsRequestList(list<Request>& requests)
{
// populate results in requests
}
or new your requests on heap with in getAsRequestList() and return a pointer
A pointer to a list or you can rewrite it as:
list<request> requests;
log_manipulator.populate_list(&requests);
Pass as in/out parameter by reference is definately my choice.
But just to give an alternative you could pass an iterator. Normally you would have to pre-size the container so that assignment by the iterator goes into a pre-allocated slot but the STL also has a back inserting iterator that you could use.
#include <iostream>
#include <iterator>
#include <list>
template<typename T>
void FillMyContainer(T inserter)
{
for(int loop=0;loop < 10;++loop)
{
(*inserter) = loop;
++inserter;
}
}
int main()
{
/*
* Example of back_inserter in use.
*/
std::list<int> data;
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(data));
/*
* Using back_inserter in the context of the question
*/
std::list<int> fill;
FillMyContainer(std::back_inserter(fill));
}
So the question now becomes why does log_manipulator not have methods to return an iterator rather than a list. Then your underlying implementation does not need to rely on a specific container type?
Declare is as
void getAsRequestList(list<Request>* requests);
And call it as
list<Request> requests;
log_manipulator.getAsRequestList(&requests);
Since you have mentioned about memory usage, Have you considering treating log_manipulator as 'iterator' or treat log_manipulator as container and write an iteator (e.g. log_iter) which iterates over it? Such that iterator operator++ will result in reading and parsing the next line from the log file and operator * will return a current 'request object'. As long as the iterator corresponds to STL iterator semantics, you can use any STL algorithms on it.
For example, You can then easily convert it to a vector using
std::vector<Request> reqvector;
std::copy(log_manipulator.begin(), log_manipulator.end(), std::back_inserter(reqvector))
(assuming begin(),end() function return a 'log_iter')
Check Writing Your Own Iterators article from Dr. Dobb's journal