I'm trying to erase specific elements from a vector of OrderPair while iterating, but it doesn't work as I expect it to. I'm trying to delete every OrderPair with id (first) that equals customer_Id. I do the following:
for(std::vector<OrderPair>::iterator i = source_Orders.begin(); i != source_Orders.end();)
{
if((*i).first == customer_Id)
{
dest_Orders.push_back(*i);
i = source_Orders.erase(i);
}
else
{
i++;
}
}
// definition of OrderPair
typedef std::pair<int, Dish> OrderPair;
// definition of OrderPair
enum DishType{
VEG, SPC, BVG, ALC
};
// definition of OrderPair
class Dish{
private:
const int id;
const std::string name;
const int price;
const DishType type;
};
When the iterator is pointing at an element that fits the if condition, (I can see when debugging that it points to the correct element), the command dest_Orders.push_back(*i); is working as intended, then I'm trying to erase the element which I moved to dest_Orders, and keep going with the next iteration. However, the result is a removal of the last element in the vector source_Orders.
What am I doing wrong here?
Thanks in advance.
There's something you are not showing us.
class Dish{
private:
const int id;
const std::string name;
const int price;
const DishType type;
};
I'm guessing that you originally got an error saying that you can't assign Dish because the members are const. So you added an assignment operator that does nothing. Yay, it now compiles.
Unfortunately, vector depends on the assignment operator actually doing what it says on the tin, because it uses assignment to move the elements around when you erase something in the middle.
Related
I'm trying to delete an item from a vector with erase() function but I keep getting an error. I
searched everywhere but can't find an answer
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
using namespace std;
class Person{
private:
string name;
public:
void set_name(string name){
this->name = name;
}
string get_name(){
return name;
}
class Record{
private:
vector <Person> book;
public:
void delete_person(string name){
for(Person p : book){
if(book.get_name() == name){
book.erase(p);
}
}
}
};
int main(){
// nothing in main yet
return 0;
}
I get en error in the delete_person() function in the record class: No matching member function for call to 'erase'
void delete_person(string name){
for(Person p : book){
if(book.get_name() == name){
book.erase(p);
}
}
}
fails for several reasons.
std::vector::erase does not accept items, it accepts iterators, locations of items to be removed.
Range-based for loops are very simple and limited in their abilities. They go from start to finish and are extremely intolerant of changes to the container while iterating. If you add or remove an item while iterating it, the hidden bookkeeping used by the loop becomes invalid and the loop breaks. And not the nice break sort of breaking. They tend to take the whole program down with them.
In Person p : book p is a new object that is a copy of an item in book. It's not the original or a reference to the original in the container. C++ defaults to values instead of references in almost every case. Unless you specifically request otherwise, you pass by value, return by value, and iterate by value.
Instead, employ the Erase-Remove Idiom. Here is an example with added commentary where I saw it fitting or educational.
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
using namespace std;
class Person
{
private:
string name;
public:
Person(const std::string & name) // added for testing
: name(name) // this is a member initializer list In C++ all class members
// and base classes must be initialized before the program can
// enter the body of the constructor. This trick allows us to
// initialize members rather than initializing them to their
// defaults (if the type has a default) and then setting them
// inside the body and wasting time doing two things where one
// thing was required
{
}
void set_name(string name) // side note consider saving construction of a new
// string and accepting name by const reference rather
// than by value and potentially making a copy.
// void set_name(const string & name)
// const because we do not intend to change `name`
// and because the compiler can take advantage of the
// promise not to change it in many interesting ways.
{
this->name = name;
}
string get_name() const // const because getters generally should not change the
// object this allows us to keep the class "const-correct"
// side note consider saving construction of a new
// string and returning by const reference rather than
// by value and making a copy.
// const string & get_name() const
{
return name;
}
};
class Record
{
private:
vector<Person> book;
public:
void add_person(const std::string & name) // added for testing
{
book.emplace_back(name);
}
void delete_person(string name) // again consider passing name by const reference
{
book.erase(std::remove_if(book.begin(), // from start of list
book.end(), // to the end
[name](const Person &p)
{
return p.get_name() == name;
}), // moves all items to be removed to the end of the
// list, then returns start of range to erase
book.end()); // erase to the end of the list
// Why erase separately? Because remove functions don't actually remove. They
// move the unwanted values to the end of the list. Looks silly, but much easier
// and safer to write. For example, this won't change the size of the list and
// break loops that count on the size to remain the same.
}
friend std::ostream & operator<<(std::ostream & out,
const Record & rec) // added for testing
{
for (const auto & item: rec.book) // print all items in book
// const because printing should not change
// the printed
// auto to let the compiler figure out the type
// & because we don't want to make a copy
{
out << item.get_name() << '\n';
}
return out;
}
};
int main()
{
Record r;
r.add_person("Bill");
r.add_person("Ted");
r.add_person("Rufus");
std::cout << r << std::endl;
r.delete_person("Ted");
std::cout << r << std::endl; // Ted should now be gone from the list
return 0;
}
Expected output:
Bill
Ted
Rufus
Bill
Rufus
book.erase(p);
book is a vector. The parameter to a vector's erase() method is an iterator.
for(Person p : book){
p is the value in the vector, and actually it is a copy of the value in the vector. You cannot pass a value to erase(). You must pass an iterator as a parameter. Passing some random copy of some random value in a vector to its erase() method is not going to accomplish anything useful.
std::vector has begin() and end() methods that return the iterator to the beginning and the end of a sequence that defines the contents of the vector.
This may be used with various algorithms, like std::find_if or std::remove_if, together with std::vector::erase to effect the removal of a value or multiple values from your vector.
I have an iterator class. Let's call it PIterator here. A MessageBuffer is iterated and is being outputted correctly, unless the nSizeOfMessage plus where the iterator currently points to is equal to the size of the whole message (position correct, index one too large).
If I check for the last element and decrement by one, it should work. Though it seems to be a "wrong way" to me. Yeah, I am not quite sure on this, so my problem is shown in this code snippet, maybe someone knows a good solution, tried to figure it out for quite a while.
Yes, I do know how to use a debugger, I know where the problem lies and it is explained just fine. I do not know how to fix this, unless used the way I mentioned.
This compiles fine under Visual Studio 2015.
Please also see the comments in the main function.
#include <iostream>
#include <vector>
class MessageBuffer
{
public:
MessageBuffer(const std::string &s)
{
_msgBuffer.assign(s.begin(), s.end());
}
char &operator[](std::size_t nIndex)
{
return _msgBuffer[nIndex];
}
//more functions...
private:
std::vector<char> _msgBuffer;
};
class PIterator
{
public:
PIterator(MessageBuffer &b)
: m_Ref(b)
, m_Where(0)
{ }
PIterator &operator=(PIterator &other)
{
if (this == &other)
return *this;
this->m_Ref = other.m_Ref;
this->m_Where = other.m_Where;
return *this;
}
//more functions...
PIterator operator+(unsigned int nValue) const
{
PIterator copy(*this);
copy.m_Where += nValue;
return copy;
}
PIterator &operator+=(unsigned int nValue)
{
m_Where += nValue;
return *this;
}
char &operator*()
{
return m_Ref[m_Where];
}
private:
MessageBuffer &m_Ref;
std::size_t m_Where;
};
int wmain(int argv, wchar_t **args)
{
std::string msg = "123MyMessage"; //Length 12
// ^ Index 3, Position 4
MessageBuffer mb(msg);
PIterator itr(mb);
//Calculations - here the results hardcoded
std::size_t nSizeOfMessage = 9; //The size of the message without the numbers
//itr.m_Where is 3 - That's where the non-numeric part of the message starts
itr += 3;
std::string needThis;
PIterator cpy = itr + nSizeOfMessage; //itr points to the first element of the message
//cpy is now out of bounds - position is correct, but index is 1 too large
needThis.assign(&*itr, &*cpy); //boom
return 0;
}
Instead of
needThis.assign(&*itr, &*cpy);
you need to use
needThis.assign(itr, cpy);
This will work if your PIterator satisfies iterator requirements.
The way you call assign, you pass pointers instead of iterators, which is valid by itself. But, to get the pointers, you dereference the iterators first. Dereferencing past-the-end iterator is undefined behavior, which is caught in Debug configuration of the compiler.
The solution I came up with was quite simple.
Instead of having a temporary iterator, I'll be using the char pointer and increment it's address by the size of the message, thus receiving always the correct last element. Should've seen that earlier.
needThis.assign(&*itr, (&*itr) + nSizeOfMessage);
I want to build a simple iterator, for example - in the class: "myVector":
#include <iostream>
using namespace std;
#define maxSize 10
class myVector {
private:
int *arr;
int sp;
public:
myVector() {
arr = new int[maxSize];
sp = 0;
}
bool add(int num) {
if (sp==maxSize) return 0;
arr[sp] = num;
sp++;
return 1;
}
};
in the Example - I built a class that produces objects of type myVector. Now I want to build iterator with an operator ++ to run on the private Array of the vector.
thank you very much
You must support std::iterator_traits<YourIterator>. The easy way is to inherit from std::iterator<?> with the appropiate arguments.
In doing so you have to decide on an iterator category. This determines what you guarantee to support, both operator wise and behaviour wise.
Now, boost has some helper types to make writing an iterator a tad easier. Consider using boost. But a basic iterator is not impossible to write without them.
In the particular case above, a pointer is a valid iterator for your problem. And easier than either of the above options. Use this as your first iteration: KISS. Note that pointers have std::iterator_traits support for free.
To make your object iterable (and support for(auto&&x:c) syntax), either write a free begin and end function in the same namespace as your class that produces iterators, or add begin() and end() methods that do the same. I tend to also add size and empty and front and back as I find them useful. As an example:
T& back(){return *std::prev(end());}
T const& back()const{return *std::prev(end());}
You need to write something like this.
class myVector {
class myIterator {
private:
int *position; //operator ++ increment this position
public:
myIterator operator++(){
//increment position here
}
int& operator*(){
//return *pos
}
bool operator==(const myIterator &it)const {
//check that pos and it.pos are the same
}
};
};
This will work but wont be a STL compliant iterator, for that you will also need to add several typedefs, to say for instance the type of your iterator (in your case you have an input iterator). If you want an STL iterator the easiest thing is to use boost facade iterator.
If I have a class:
class T{
public:
int Id;
//some methods, constructor..
}
and in other class:
vector<T> collection;
and want to a write a method:
T& getValue(int Id){
//scanning the vector till i find the right value
}
The problem is that scanning the vector through iterator always give a const value so I get an error about qualifiers. So how do I get a value from a vector? but Not a const one.
EDIT: According to the Answers I tried to so something like this:
Group& Server::findGroup(unsigned int userId) const{
for(auto iterator=groups.begin();iterator!=groups.end();++iterator){
if(iterator->isInGroup(userId)){
return (*iterator);
}
}
//throw exception here
}
the definition of groups:
vector groups;
This is exactly the same example I gave at first but now T is Group.
The following code should give you a non-const iterator and work fine:
for(vector<T>::iterator i = collection.begin();
i != collection.end(); ++i) {
if(Id != i->Id)
continue;
// Matching ID! do something with *i here...
break;
}
If this doesn't help, please explain in more detail what is broken.
The problem here is the const in your declaration:
Group& Server::findGroup(unsigned int userId) const //<==THIS
That means that this is a const Server*, and thus everything in it is const as well, including groups. Which means that groups.begin() will return a const_iterator instead of an iterator.
One thing you can do (might not be a good idea; you need to be really sure!) would be to mark groups as mutable, which lets it be changed even if its enclosing object is const:
mutable vector<T> groups;
Doing this will make groups.begin() return a regular iterator.
But I would instead ask you to reevaluate why this method is declared const at all, since you're returning part of the object in a form that can be changed and thus you're not really honoring const.
Problem:
Error: The "&" operator can only be applied to a variable or other l-value.
What I've tried:
dynamic_cast<char*>(e)
reinterpret_cast<char*>(e)
static_cast<char*>(e)
(char*) e
What I'm trying to do:
Write the array e.data (private) to a binary file.
Notes:
e.getSize() returns number of elements in array
e[] returns Employee object.
Code:
fstream fout;
fout.open(filename.c_str(), ios::out|ios::binary);
if(fout.good())
{
for(int i=0;i<e.getSize();i++)
{
fout.write((char*)&e[i], sizeof(e[i]));
}
}
fout.close();
Employee.h
class Employee {
friend std::ostream& operator<<(std::ostream&, const Employee &);
private:
int id;
char name[50];
char address[100];
char phone[20];
char department[100];
int salary;
public:
Employee();
~Employee();
Employee(int,char[],char[],char[],char[],int);
bool operator==(Employee&);
};
I'm lost at what to do, from what I remember fout.write((char*)&e[i], sizeof(e[i])); is how to write to binary files.
Edit:
e is declared like so:
MYLIB::Bucket<Employee> e;
template <class T>
class Bucket {
private:
T* bkt;
int size;
int capacity;
static const int stepsize = 10;
public:
Bucket();
~Bucket();
void push_back(const T&);
T operator[](int);
int getSize();
int getCapacity();
};
Edit 2:
fout.write(reinterpret_cast<char*>(e[i]), sizeof(e[i])); gives me line 122: Error: Using reinterpret_cast to convert from ? to char* not allowed. (Line 122 is the line just quoted)
Edit 3:
tempemp = e[i];
fout.write((char*)(&tempemp), sizeof(e[i]));
Compiles but gives a segmentation fault, I'll investigate why.
Compiles, the segmentation fault looks unrelated.
MYLIB::Bucket<Employee> e;
this seems to be a container. e[i] gives you an Employee by value. you need to get this object's address using &e[i] but you can'd do that since it's an r-value so you need to copy it to a non r-value:
Employee copye = e[i];
fout.write((char*)©e, sizeof(e[i]));
Should work.
On a side note, this all looks like terrible code and I don't envy whoever needs to maintain or read it. A few points:
you should not be using the binary format of the in-memory object as your serialization format. Use a proper serialization format like protobuf or xml or json
Why pull your own strange containers when you can use std::vector std::list ? re-inventing the wheel is always bad
returning an element by value from a container creates copies which degrade performance.
I think that
T operator[](int);
is returning a temporary object that must be bound to something before an address can be taken
const Employee& emp = e[i];
fout.write((char*)&emp, sizeof(emp));
might work, assuming this answer is correct that taking a reference extends the life of a temporary object
An alternative might be to return a reference to the object, which would remove the creation of temporary objects
const T& operator[](int);