Cannot iterate through map in print() method - c++

I've done some exercises before where I loop through maps no problem. Now that I'm doing so in a class, I'm getting strange errors that I don't understand at all. It seems to happen at the instantiation of the iterator in the print() method.
error: conversion from ‘std::mapstd::__cxx11::basic_string<char,
bool>::const_iterator’ {aka
‘std::_Rb_tree_const_iterator<std::pair<const
std::__cxx11::basic_string, bool> >’} to non-scalar type
‘std::mapstd::__cxx11::basic_string<char, bool>::iterator’ {aka
‘std::_Rb_tree_iterator<std::pair<const
std::__cxx11::basic_string, bool> >’} requested 33 | for
(map<string, bool>::iterator i = jobs.begin(); i != jobs.end(); i++) {
Here is my code:
class Employee {
private:
string name;
map<string, bool> jobs;
public:
Employee() {
name = "";
jobs[""] = false;
}
Employee(const Employee &other) {
cout << "Copied." << endl;
name = other.name;
jobs = other.jobs;
}
Employee(string name, string task, bool trained) {
this->name = name;
jobs[task] = trained;
}
void setTask(string task, bool trained) {
jobs[task] = trained;
}
void print() const {
for (map<string, bool>::iterator i = jobs.begin(); i != jobs.end(); i++) {
pair<string, bool> jobs = *i;
cout << name << " is trained on " << jobs.first << "? " << jobs.second << endl;
}
}
};
I promise I did my best to look for solutions online before coming here. Sorry if I'm misunderstanding something extremely elementary.
I've tried making the print method const or not const, and I've experimented with using const_iterator and .cbegin()/.cend() in case it was being picky about that. Sadly, I'm just not very good at debugging yet.

void print() const
That const in the end means that this class method is a const class method. It can be called on const instances of this class. What it means is that, as far as this method is concerned, all members of its class are const.
map<string, bool>::iterator i = jobs.begin();
Since this is const, the begin() overload returns a const_iterator, instead of iterator, and that's the reason for your compilation error.
I've tried making the print method const or not const,
But what you haven't tried to do is use the current C++ standard, you are apparently using a very, very outdated textbook to learn C++. Current C++ makes all of this much easier:
void print() const {
for (auto &job:jobs)
cout << name << " is trained on " << job.first << "? " << job.second << endl;
}
And that's not even the best way to do it. I'll leave it to you to get your textbook updated, and learn about structured bindings that makes this even cleaner to read.

Your print member function is declared as const, so all occurrences/uses of data members of the class made within it will also be treated as const.
Thus, the calls to jobs.begin() and jobs.end() will invoke the const overloads of those functions, which have return types of const_iterator. So, to fix your issue, make i a const_iterator:
void print() const {
for (map<string, bool>::const_iterator i = jobs.begin(); i != jobs.end(); i++) {
pair<string, bool> localjob = *i; // Avoid "shadowing" member variable!
cout << name << " is trained on " << localjob.first << "? " << localjob.second << endl;
}
}

The member function print is a constant member function. It means that within the function data members of the class are constants. So instead of map<string, bool>::iterator you need to use map<string, bool>::const_iterator
Also it is a bad idea to redeclare the name jobs within the for loop. And moreover this declaration is redundant.
You should write
void print() const {
for (map<string, bool>::const_iterator i = jobs.cbegin(); i != jobs.cend(); i++) {
cout << name << " is trained on " << i->first << "? " << i->second << endl;
}
}
If your compiler supports C++ 17 then you could use range-based for loop the following way
void print() const {
for ( const auto &[first, second] : jobs ) {
cout << name << " is trained on " << first << "? " << second << endl;
}
}

Related

C++ using vector iterator correctly

I'm new to C++ and I have a vector of doctors.
I add a new doctor with the following code:
void DoctorAdmin::setDoctor(std::string lastname, std::string forename,
Person::Sex sex){
//Create new doctor
Doctor* doc = new Doctor(lastname, forename, sex);
//insert at the end of the vector
doctors.push_back(doc);
}
Then I want to show their information on the console:
void DoctorAdmin::showDoctors(){
cout << "Doctors:" << endl;
cout << "Name" << "\t\t\t" << "Forename" << "\t\t\t" << "Sex" << endl;
for (vector<Doctor*>::iterator i = doctors.begin(); i != doctors.end(); i++){
Doctors* doc = doctors.at(i);
cout << doc->getName() << "\t\t\t" << doc->getForename() << "\t\t\t"
<< doc->getSex() << endl;
}
After doing it like this I get two Errors:
E0304 No instance of overloaded function "std::vector<_Ty, _Alloc>::at [mit _Ty=Doctors *, _Alloc=std::allocator<Doctors *>]" matches the argument list.
// and
C2664 "Doctors *const &std::vector<Doctors *,std::allocator<_Ty>>::at(const unsigned int) const" : cannot convert from Argument "std::_Vector_iterator<std::_Vector_val<std::_Simple_types<_Ty>>>" in "const unsigned int"
How do I use the vector iterator correctly to avoid this?
An iterator is not index-like, it is pointer-like.
for (vector<Arzt*>::iterator doc = aerzte.begin(); doc != aerzte.end(); doc++)
{
cout << (*doc)->getName() << "\t\t\t" << (*doc)->getVorname() << "\t\t\t"
<< (*doc)->getGeschlecht() << endl;
}
It seems like you are confused as to when you need to new things too. Most of the time you don't need new
vector<Arzt> aerzte;
void ArztAdmin::anlegenArzt(std::string name, std::string vorname, Person::Geschlecht geschlecht){
// Create new doctor at the end of the vector
aerzte.emplace_back(name, vorname, geschlecht);
}
You can also directly bind references as loop variables
for (Arzt & doc : aerzte)
{
cout << doc.getName() << "\t\t\t" << doc.getVorname() << "\t\t\t"
<< doc.getGeschlecht() << endl;
}
The at function requires an index, but a vector<Arzt*>::iterator is not an index, neither semantically nor technically. An iterator points directly to an element, whereas an index represents the distance between a container's start and the element in a container that allows random element access.
Because an iterator points directly to an element, the at function isn't even necessary in your loop. *i yields the element:
Arzt* doc = *i;
Beginning with C++11, the code for such simple loops can be written in a shorter way using auto:
for (auto i = aerzte.begin(); i != aerzte.end(); i++){
The compiler knows what type i really is because it knows what begin() returns.
Even better, use a range-based loop:
for (auto doc : aerzte){
cout << doc->getName() << "\t\t\t" << doc->getVorname() << "\t\t\t"
<< doc->getGeschlecht() << endl;
}
And while we're at it, don't use dynamic memory allocation when you don't have to. This isn't Java or C#; new is dangerous territory in C++ and should be avoided:
#include <vector>
#include <string>
#include <iostream>
struct Arzt
{
Arzt(std::string const& name, std::string const& vorname) :
name(name),
vorname(vorname)
{
}
std::string name;
std::string vorname;
// Geschlecht omitted for brevity's sake
};
int main()
{
std::vector<Arzt> aerzte;
Arzt doc1("foo", "bar");
Arzt doc2("foo", "bar");
Arzt doc3("foo", "bar");
aerzte.push_back(doc1);
aerzte.push_back(doc2);
aerzte.push_back(doc3);
for (auto const& arzt : aerzte)
{
std::cout << arzt.name << ' ' << arzt.vorname << '\n';
}
}
As you are no longer iterating over pointers but over larger objects, const& should be used in the for loop.

Weird behavior of std::pair second when returned from a function

#include<boost/unordered_map.hpp>
#include<string>
#include<iostream>
#include<boost/unordered_set.hpp>
using namespace std;
typedef boost::unordered_map<string, boost::unordered_map<string, boost::unordered_set<string>>> nfa;
const boost::unordered_map<string, boost::unordered_set<string>>&
get_second(const std::pair<string,
boost::unordered_map<string, boost::unordered_set<string>>>& p)
{return p.second;}
int main()
{
nfa a;
a["A"]["0"] = {"B", "C"};
a["A"]["1"] = {"B"};
a["B"]["0"] = {"B"};
a["B"]["1"] = {"C"};
cout << "Printing using direct reference" << endl;
for (auto tr_table : a)
{
for (auto tr : tr_table.second)
cout << tr_table.first << " " << tr.first << " " << tr.second.size() << endl;
}
cout << "Printing using function get_second" << endl;
for (auto tr_table : a)
{
for (auto tr : get_second(tr_table))
cout << tr_table.first << " " << tr.first << " " << tr.second.size() << endl;
}
return 0;
}
For the same unordered_map, using tr.second returns the correct number of rows but using get_second returns a new map element with no elements.
What is the reason for this behavior?
I am using g++ 5.3.1 on Ubuntu.
PS: The behavior is same when std::unordered_map is used.
get_second takes a pair of the wrong type, with a non-const key.
Therefore a converted temporary is constructed and you are returning a reference to this temporary.
All bets are off after that.
Your get_second method parameter doesn't match the loop iterator in terms of constness... update as follows (note const string in pair) and it works:
get_second( const std::pair<const string,
unordered_map<string, unordered_set<string>>>& p )
Be noted that std::unordered_map's value_type is std::pair<const Key, T> (it's const Key), so your get_second()'s parameter is wrong.
You can simply change to get_second(const nfa::value_type& p) to get correct behavior.

How to print a map

I am trying to print a map in an organized way. My map is defined like this:
map<std::string,std::vector<message *> > data;
where message is a struct like this:
struct message{
static unsigned int last_id;
unsigned int id;
std::string msg;
std::string timestamp;
message(const std::string& recvbuf_msg,const std::string& a_timestamp) :
msg(recvbuf_msg), timestamp(a_timestamp), id(++last_id)
{
}
};
I tried this way of printing it:
std::cout << (data[username]).at(0)->msg << std::endl;
But it gives a debug error when reaching that function, how can i solve it?
Error R6010 - abort() has been called suggests that either there is no entry for key username in the map, or the vector of messages for that user is empty. You need to make sure the containers are nonempty before accessing elements. It is a good idea to use iterators, here is an example of how to print the messages for all usernames:
for(auto mapIt = data.cbegin(); mapIt != data.cend(); ++mapIt)
{
std::cout << "printing data for " << mapIt->first << ":" << std::endl;
for(auto vectIter = mapIt->second.cbegin(); vectIter != mapIt->second.cend(); ++vectIter)
{
std::cout << (*vectIter)->msg << ", " << (*vectIter)->timestamp << ", "
<< (*vectIter)->id << std::endl;
}
}
The code uses auto, so if you are not using a C++11 compliant compiler, you will have to write the iterator types yourself.

Accessing one "item" of a map

I wanted to access one "item" of my map<wstring,wstring>.
But this does not work:
for (unsigned int i=0;i<m_ValidCharacterTranslations.Content().size();i++)
{
wstring wsFirst = &m_ValidCharacterTranslations.Content()[i]->first;
wstring wsSecond = &m_ValidCharacterTranslations.Content().at(i)->second;
//do something with these 2 wstrings
}
The error I am getting in the last line is:
No binary operator accepts the right-handed operand of type 'unsigned int'.
My class is declared like this:
clsTranslations m_ValidCharacterTranslations;
class clsTranslations : public CBaseStructure
{
private:
map<wstring,wstring> m_content;
protected:
virtual void ProcessTxtLine(string line);
public:
map<wstring,wstring> &Content();
void LoadTranslations(string file);
};
Can somebody tell me how to get these values?
I would like to iterate through the map and use the first and the
second wstring of the map.
C++11:
for (auto& kvpair : somemap) {
cout << kvpair.first << " has value " << kvpair.second << std::endl;
}
pre C++11:
for (map<wstring,wstring>::iterator it = somemap.begin(); it != somemap.end(); it++) {
cout << it->first << " has value " << it->second << std::endl;
}
You access first and second addresses, instead of values.

C++ STL set of classes - compiler error error C2664

Newbie programmer here trying to work out his homework. I'm trying to use a STL set of classes, but the compiler complains about my code.
car.h
#include <string>
#include <iostream>
#include <time.h>
#include <set>
class Car
{
private:
std::string plateNumber;
std::string description;
std::string dateIn;
std::string timeIn;
public:
Car() {};
~Car() {};
Car(std::string plate, std::string desc)
{
plateNumber = plate;
description = desc;
};
void setPlateNumber(std::string plate) ;
std::string getPlateNumber() const;
void setDesc(std::string desc);
void setTimeDateIn() ;
std::string getTimeIn() const;
std::string getDateIn() const;
std::string getDesc() const;
friend std::ostream & operator<<(std::ostream & os, Car &c);
};
std::ostream & operator<<(std::ostream & os, Car& c)
{
os << "Plate Number: " << c.plateNumber << ", Date In: " << c.dateIn << ", " <<
`"Time in: " << c.timeIn << "Description: " << c.description << std::endl;
return os;
}
bool operator< ( const Car& lhs, const Car& rhs)
{
return ( lhs.getPlateNumber() < rhs.getPlateNumber() );
};
main.cpp
#include "stdafx.h"
#include <iostream>
#include <set>
#include <string>
#include "car.h"
void carEnters(std::set<Car> g);
void carLeaves(std::set<Car> g);
void displayContents(std::set<Car> g);
int main ()
{
char choice [80];
// initialize the sets and iterators
std::set<Car> garage;
do // Loop until user quits
{
std::cout <<
std::endl;
std::cout << "Menu:" << std::endl;
std::cout << "-----" << std::endl;
std::cout << "'1' to enter a new car, or " << std::endl;
std::cout << "'2' to exit the front car, or " << std::endl;
std::cout << "'3' to to list all the cars or." << std::endl;
std::cout << "'0' to close the garage: " << std::endl;
std::cin.getline( choice, 1, '\n');
switch ( choice[0] )
{
case '0' :
std::cout << std::endl << "Thanks for playing...\n";
break;
case '1' :
carEnters(garage);
break;
case '2' :
carLeaves(garage);
case '3' :
displayContents(garage);
break;
default:
std::cout << "I'm sorry, I didn't understand that.\n";
break;
}
} while ( choice[0] != '0' ); // Loop again if the user hasn't quit.
return 0;
}
void carEnters( std::set<Car> g)
{
// Car enters garage
std::cout << "Please enter the plate number:" << std::endl;
std::string plate;
std::cin >> plate;
std::cin.ignore();
std::set<Car>::iterator findPlate;
Car* lookup = new Car;
lookup->setPlateNumber(plate);
findPlate = g.find(*lookup);
if (findPlate != g.end()) // Add car to garage
{
Car *currentCar = new Car ;
// Set car parameters
std::cout << "Please type the entering car's description <Model, Color...
> : " << std::endl;
char desc[80];
std::cin.get(desc, 80 );
std::cin.ignore();
currentCar->setDesc(desc);
currentCar->setTimeDateIn();
currentCar->setPlateNumber(plate);
g.insert(currentCar);
}
else // Plate is already in garage set
{
std::cout << "Sorry, this car is already in the garage!" <<
std::endl;
}
}
void carLeaves( std::set<Car> g)
{
std::string plate;
std::cout << "Which plate is leaving?" << std::endl;
std::cin >> plate;
std::cin.ignore();
// Find car's plate number in the garage set
// for (findPlate=garageSet.begin(); findPlate !=garageSet.end(); findPlate++)
std::set<Car>::iterator findPlate;
Car lookup(plate,"");
findPlate = g.find(lookup);
if (findPlate != g.end())
{
// Display time in and then remove car from set of cars
std::cout << "Car out at " << (*findPlate).getDateIn() << ", " <<
(*findPlate).getTimeIn() << std::endl;
g.erase(findPlate);
}
else
{
std::cout << "Car was not found in set of Cars!" << std::endl;
}
}
// Car class function implementation
void Car::setPlateNumber(std::string p)
{
plateNumber = p;
}
std::string Car::getPlateNumber() const
{
return plateNumber;
}
void Car::setDesc(std::string d)
{
description = d;
}
void Car::setTimeDateIn()
{
char dat[9];
char tim[9];
_strdate_s(dat);
_strtime_s(tim);
dateIn=dat;
timeIn=tim;
}
std::string Car::getTimeIn() const
{
return timeIn;
}
std::string Car::getDateIn() const
{
return dateIn;
}
std::string Car::getDesc() const
{
return description;
}
// Display the car set
void displayContents(std::set <Car> garage)
{
// function displays current contents of the parking garage.
std::set <Car>::iterator carIndex;
std::cout << std::endl << "Here are all the cars parked: " << std::endl;
for (carIndex = garage.begin();
carIndex != garage.end();
++carIndex )
{
std::cout << " " << carIndex->getPlateNumber() << ", Date In: " <<
carIndex->getDateIn() << ", " << "Time In: " << carIndex->getTimeIn() << "Description:
" << carIndex->getDesc() << std::endl;
}
}
The error I get from the compiler is this:
xmemory(208): error C2664: 'Car::Car(const Car &)' : cannot convert parameter 1 from 'Car *' to 'const Car &'
Reason: cannot convert from 'Car *' to 'const Car'
No constructor could take the source type, or constructor overload resolution was ambiguous
I'm not sure where I'm going wrong, would some please point out how my overload is incorrect?
Thanks
The error is likely the g.insert(currentCar) line in the carEnters method, as g is a std::set<Car>, not a std::set<Car*>. Either pass in a reference to the current car (*currentCar) or make the garage contain pointers to cars.
In addition, you may wish to pass in g as a reference, in the form of...
void carEnters(std::set<Car>& g) { }
void carLeaves(std::set<Car>& g) { }
Otherwise the set is being copied and you might not get the results you want.
If you need explanation as to the why for any of these, add a comment. I used to do some TAing back in the day. :)
I believe #James is on the right track, but passing *CurrentCar isn't really the right answer (at least IMO). Instead, you should back up a bit:
Car *currentCar = new Car ;
Perhaps you have prior experience with Java (or something similar) where this is a routine, normal type of code to write. In C++, however, using new directly is (or at least should be) fairly unusual. What you almost certainly want instead is:
Car currentCar;
and then you'll fill in the fields like:
currentCar.whatever = x;
Then, when you put your currentCar into the std::set (or whatever) you won't have to dereference anything, because you'll be starting with a Car object, which is what's expected. As an aside, I'd note that when you look up the car, you're also creating a Car object dynamically -- but you never seem to delete either one, so you're code is leaking memory.
Edit: I should add that there are alternatives that may be preferable. Right now, you're basically treating a Car as "dumb data", with outside code to operate on that data. If you want your code to be "object oriented", it would almost certainly be better to move the code for reading a Car's data into the class itself, so outside code would just invoke that member function.
Another possibility would be to make a Car an immutable object. Instead of creating an unitialized car, and then setting the appropriate values in that object, I'd pass the correct values to Car's constructor, and eliminate the member functions you currently have for changing those values. At least for your purposes, it doesn't appear that you need to actually change a car's plate number -- it should apparently only ever have one plate number, in which case it would be better for your code to reflect (and enforce) that directly.
Your problem is that your set takes elements of type Car but you are inserting elements of type Car*:
void carEnters( std::set<Car> g)
{
...
Car *currentCar = new Car;
...
g.insert(currentCar);
In this case, currentCar is a pointer to a Car and g.insert expects a Car. There are multiple ways of fixing this - you can change your set to use Car* although your overloaded operator< will no longer work (you'll have to create a functor that is passed to the set and takes two Car*s). You can change currentCar to be of type Car. This results in a bunch of copying however. Or you can ditch currentCar entirely and make a constructor that will set all the variables you need set:
Car(const std::string &plate, const std::string &desc)
{
plateNumber = plate;
description = desc;
setTimeDateIn();
};
then you can just do this:
g.insert(Car(desc, plate));
Which is actually preferable to what you are doing now, as someone might forget to call setTimeDateIn. It makes more sense for that to be called when the Car is constructed.