I want to insert object with container list - c++

I want to add contact on some index and i can't figure it out why is this code not working.
I have problem with this line:
a = (*it);
and this one:
tmp.push_back(listOf[a]);
This is the code:
void insert(list<Contact> &listOf, int index, const Contact &info) {
list<Contact> tmp;
int a;
for(list<Contact>::iterator it = listOf.begin(); it != listOf.end(); it++)
{
a = (*it);
if(a == index)
{
tmp.push_back(info);
index -= 2;
it--;
}
else
tmp.push_back(listOf[a]);
}
listOf = tmp;
}
I have structure Contact with name and surname (string).
Maybe is something wrong with just those two lines? Or is it entire code wrong? If it is, i would be glad if someone can get me a solution.

The problem here is that you expect that a list is the same as a vector, and can be used interchangeably.
First of all when you dereference an iterator, it returns the the type of the container, in your case an instance of Contact. Secondly, you can't use array-like indexing on a std::list.
To solve the first problem, you have to keep track of indexes yourself.
To solve the second problem, you already have the iterator and can use that in the push_back call:
tmp.push_back(*it);

It seems that class (or structure) Contact has no conversion function that converts an object of type Contact to an object of type int. So this code is invalid
int a;
//,,,
a = (*it);
because the type of expression *it is Contact not int.
Also class std::list has no subscript operator. So this statement
tmp.push_back(listOf[a]);
is also invalid and the compiler shall issue an error.

Related

Efficiently remove last element from std::list

This seems like a simple problem, and it is certainly doable, but I'd like to do it efficiently.
The Objective:
Remove the last element from a std::list if it meets a condition.
The Problem:
My compiler (MSVC++ 10) is unhappy about casting a reverse iterator to a const iterator for a method call to std::list.erase(). The message is:
error C2664: 'std::_List_iterator<_Mylist>
std::list<_Ty>::erase(std::_List_const_iterator<_Mylist>)' : cannot
convert parameter 1 from 'std::reverse_iterator<_RanIt>' to
'std::_List_const_iterator<_Mylist>'
The Code I Tried:
std::list<mytype> mylist;
// lots of code omitted for clarity
bool ends_badly = true;
while(ends_badly && mylist.size() > 0)
{
auto pos = mylist.crbegin(); // Last element in the list
if ((*pos)->Type() == unwanted)
{
mylist.erase(pos); // Here is where the compiler complains
}
else
{
ends_badly = false;
}
}
I can get around this by using forward iterators and looping through the list to the end, but that's so cumbersome. The compiler is OK with a forward iterator in this context, and I tried casting a the reverse iterator to a const iterator but the compiler didn't like that either.
Erasing a list element from a bidirectional list using a reverse iterator seems like a reasonable thing. Is there something obvious I'm missing here?
I suppose that you can simplify your code snippet doing it the next way:
while (!mylist.empty() && mylist.back()->Type() == unwanted) {
mylist.pop_back();
}
To fix the specific error in your code Can I convert a reverse iterator to a forward iterator?
mylist.erase((pos+1).base());
Using std::reverse_iterator::base
The base iterator refers to the element that is next (from the std::reverse_iterator::iterator_type perspective) to the element the reverse_iterator is currently pointing to.
Anyway, pop_back is the best choice in your case.

std::find_if for custom objects returns bad iterator when vector has at least 1 element

So I have a vector of objects of type Player. If I try to use std::find_if on that vector and use a lambda expression that return true only if the name if the player is a name I want to check against, it will work the first time if the vector is empty (as in the iterator is nullptr), but once I add a Player object in the vector, the find_if will return an iterator filled with random, bad data.
My code:
auto player = find_if(m_playerList.begin(), m_playerList.end(), [name](Player& p)
{
return p.Name == name;
});
if (player._Ptr != nullptr)
{
// Do stuff
}
else
{
Player newPlayer;
newPlayer.Name = name;
m_playerList.push_back(newPlayer);
}
So in the first iteration of this function, the player is nullptr, because the vector didn't contain any elements. In the second iteration, if I search by the same name, it finds it in the vector, however if I search by a different name, it returns an object with mangled, random data and the check "if (player._Ptr != nullptr)" passes.
Question is, what causes this? Am I checking the "player" object properly to figure out if the find_if actually found a valid object in the vector?
The code you wrote isn't portable because it uses implementation-specific _Ptr member of vector::iterator, and therefore there are no requirements on that member to be nullptr. Change
if (player._Ptr != nullptr)
to
if (player != m_playerList.end())

C++: How to set an iterator to a list inside a struct inside another list

I'm new to c++ and this is my first post. I have a question regarding lists.
I have a struct:
struct record
{
list<string> courseList;
};
There are other things in the list, but they are not pertinent. I also have a list of records
list<record> studentList;
My question is, how do I set an iterator to courseList.begin()?
I have tried:
list<record>::iterator sl; //studentList
list<record>::iterator cl; //courseList
cl = (*sl).courseList.begin();
But I get an error "no match for 'operator!='.
list<record>::iterator sl; //studentList
This is a default constructed iterator, meaning it doesn't refer to anything yet and so is not dereferenceable, and you can only use it in very limited ways.
list<record>::iterator cl; //courseList
This is also a default-constructed iterator, and if it's meant to refer into a courselist then it has the wrong type, it should be list<string>::iterator.
cl = (*sl).courseList.begin();
This tries to dereference the non-dereferenceable iterator (which is very bad, your program will crash if you're lucky. If you're unlucky it will do something worse).
If the iterator actually was dereferenceable then the result of the expression would be a list<string>::iterator, which can't be assigned to cl because that is a different type.
You probably want something like this:
list<record>::iterator sl = studentList.begin();
if (sl != studentlist.end())
{
list<record>::iterator cl = (*sl).courseList.begin();
if (cl != sl->courselist.end())
{
// do stuff with the iterator
}
}
The initializer for cl can also be written like this , which is equivalent and probably more conventional:
list<record>::iterator cl = sl->courseList.begin();
This question has been answered. Amature mistake, I made cl a list::iterator instead of list. Thank you everyone
you should use 2D vector for this:
vector < vector<string> >ele(10);
Then insert elements like this:
ele.at(0).push_back("abc");
ele.at(0).push_back("def");
And to use iterators
ele.at(0).begin();
ele.at(0).end();

const_pointer_cast of a std::set iterator

I've got a bit of a conundrum here. I am writing a mesh (grid) generator. In doing so, I need to obtain a list of lower dimensional elements from higher dimensional elements (i.e. a "triangle" element is composed of 3 "line" elements). I need the list of unique line elements and I need the parent element to store a pointer to each of its unique child elements.
To this end, I create a std::set of smart pointers to the child elements and try to insert each child element to the list. Every time I try to insert an element I ask for a pair containing an iterator to the pre-existing element and a boolean specifying whether or not the element has been inserted.
The problem is that std::set (and map) return constant iterators to the pre-existing element. However, I need to give the parent element of the element that I failed to insert a non-constant pointer to the pre-existing element. So I use const_pointer_cast<> to cast the constant iterator to a non-constant one.
Is there a better way of going about this? Doing a const_pointer_cast() is probably not ideal. Is there something that I should be using for this application instead of std::set?
Edit: here's the function:
template<class T1, class T2>
int getUniqueSubelements(std::set<std::shared_ptr<T1>, elGreater>& elements,
std::set<std::shared_ptr<T2>, elGreater>& childels)
{
typedef std::shared_ptr<T2> child_ptr;
std::pair<typename std::set<child_ptr, elGreater>::iterator, bool> ret;
for (auto parit = elements.begin(); parit != elements.end(); ++parit)
{
(*parit)->generateChildren();
const std::vector<child_ptr>& children = (*parit)->getChildren();
for (int i = 0; i < children.size(); i++)
{
ret = childels.insert(children[i]);
if (ret.second == false)
{
child_ptr temp = std::const_pointer_cast<T2>(*ret.first);
bool orient = elOrientation(children[i], temp);
children[i]->swapInParent(temp, orient);
}
}
}
return 1;
}
No cast at all is needed here.
It's true that std::set<K, Comp>::iterator is the same as std::set<K, Comp>::const_iterator, and that iterator::operator* returns a const K&.
But in your case, this means that the type of *ret.first is const std::shared_ptr<T2>&. This is the analogue of (T2* const), not (const T2*): you can't modify the smart pointer, but you can modify the actual stored object.
So assuming elOrientation is something like
template <typename T>
bool elOrientation(std::shared_ptr<T>, std::shared_ptr<T>);
you can just call elOrientation(children[i], *ret.first).

Weird bug while inserting into C++ std::map

I'm trying to insert some value pairs into a std::map.
In the first case, I receive a pointer to the map, dereference it and use the subscript operator to assign a value. i.e.
(*foo)[index] = bar;
Later, when I try to iterate over the collection, I'm returned key/value pairs which contain null for the value attribute in all cases except for the first (map.begin()) item. The weird thing is, if I do the insertion via the map's insert function, everything is well, i.e:
foo->insert(std::pair<KeyType,ValueType>(myKey, myValue));
Why would this be? Aren't the two methods functionally equivalent? I've pasted some snippets of actual code below for context
...
typedef std::map<int, SCNode*> SCNodeMap;
...
void StemAndCycle::getCycleNodes(SCNodeMap* cycleNodes)
{
(*cycleNodes)[root->getId()] = root;
SCNode* tmp = root->getSucc();
while(tmp->getId() != root->getId())
{
// (*cycleNodes)[tmp->getId()] == tmp; // crashes (in loop below)
cycleNodes->insert(std::pair<int, SCNode*>(tmp->getId(), tmp));//OK
std::pair<int, SCNode*> it = *(cycleNodes->find(tmp->getId()));
tmp = tmp->getSucc();
}
// debugging; print ids of all the SCNode objects in the collection
std::map<int, SCNode*>::iterator it = cycleNodes->begin();
while(it != cycleNodes->end())
{
std::pair<int, SCNode*> p = (*it);
SCNode* tmp = (*it).second; // null except for it = cycleNodes->begin()
std::cout << "tmp node id: "<<tmp->getId()<<std::endl;
it++;
}
}
I'm all out of ideas. Does anyone have a suggestion please?
In your actual code you have:
(*cycleNodes)[tmp->getId()] == tmp;
This will not assign tmp into the map, but will instead reference into the map creating an empty value (see #Neil Butterworth) - you have == instead of =. What you want is:
(*cycleNodes)[tmp->getId()] = tmp;
You should be aware that operator[] for std::map will insert a value into the map if one does not exist when used in expressions like this:
if ( amap[x] == 42 ) {
...
}
If the value x does not exist, one will be created and assigned the value created by the value types default constructor, or zero for the built-in types. This is almost never what you want, and you should generally avoid the use of operator[] with maps.
Does your value type have an assignment operator?
Take a look at this reference. The [] operator returns a non-const reference to the value. If your assignment is wrong or somehow works in an unexpected way this might be the cause.
The insert method on the other hand takes a value and stuffs it into the map. The [] operator constructs an object with the default constructor, then let's you assign stuff to it using it's assignment operator.