How to iterate through map when pass a class to map - c++

I am writing a simple code that. I've created two objects from class Employee that have two method. getData() and displayData(), and I've made a map of Employee type. It is taking input but to iterate through map by calling the displayData() that is in class.
Here is the code. Ive got the error that error: 'struct std::pair<const int, Employee>' has no member named 'displayData'
Any suggestions or am i doing it wrong.
#include <iostream>
#include <map>
using namespace std;
class Employee {
private:
int id;
string name;
public :
void getData(){
cin >> id >> name;
}
void displayData(){
cout << id <<" "<<name<<endl;
}
};
int main() {
map<int, Employee> m1;
map<int, Employee> :: iterator it;
Employee e1,e2;
//for (i)
e1.getData();
m1[1] = e1;
e2.getData();
m1[2] = e2;
for (it = m1.begin(); it!=m1.end(); ++it){
cout << (*it).first.displayData() << (*it).second.displayData() <<endl;
}
return 0;
}
Thanks for the help in advance

Here is the code. Ive got the error that error: 'struct
std::pair<const int, Employee>' has no member named 'displayData'
The compiler tells you exactly what is wrong, you may have a Misconception about std::maps, std::maps holds a key-value pair(More details About Maps) which is a std::pair which can be accessed via an iterator or the value accessed directly via map[key]. iterator::first holds the key which in your code is an int, while iterator::second holds the value which in your case right now is an object called Employee, hence (*it).first.displayData() error.
This line is also incorrect, and would produce compiler errors.
cout << (*it).first.displayData() << (*it).second.displayData() <<endl;
(*it).second.displayData() will return the return type of the function since this is a function call, in this case a type of void. You can simply call the function like this to achieve your desired result in every iteration.
std::cout << it->first << " ";
it->second.displayData();
Always trust compiler errors.

In C++ 17, you can use structured bindings for more readbility:
for (auto [eid, employee] : m1)
{
std::cout << eid << ": ";
employee.displayData();
}

Related

insertion into unordered_map got lost

I'm having the following code, but after run the code, the result is empty, any ideas why the result is empty? the reference of result in function main was passed to myclass, I thought function addToResult will actually add data to result, and I'm expecting a map key = "test", value = "1": "1". I'm kind of new to c++. Thanks!
#include <iostream>
#include <string>
#include <unordered_map>
using LookUpTable = std::unordered_map<std::string, std::string>;
using DLTable = std::unordered_map<std::string, LookUpTable>;
class MyClass
{
public:
MyClass(DLTable& dltable) {
m_dltable = dltable;
};
void addToResult() {
LookUpTable ee;
ee.emplace("1", "1");
m_dltable.emplace("test", ee);
};
private:
DLTable m_dltable;
};
int main ()
{
DLTable result;
MyClass myclass(result);
myclass.addToResult();
std::cout << "myrecipe contains:" << std::endl;
for (auto& x: result) {
std::cout << x.first << ": "<< std::endl;
for (auto& xx : x.second) {
std::cout << xx.first << ": " << xx.second << std::endl;
}
}
std::cout << std::endl;
return 0;
}
Let' look into simplified example:
int a = 0;
int &b = a;
int c = b;
c = 123;
Will last assignment modify a? Of course not. It does not matter how you pass value to c through reference or not c is completely independent variable that just initialized by a reference.
Your case is the same - m_dltable is separate variable and the fact you initialize it using reference does not change anything. (Your case even worse, you did not initialize it by reference, you assigned to it)
In general your approach is wrong. If you want directly access that variable then just make it public, do not try to create convoluted workarounds on how to access it. If you want incapsulation just create members that allow you to iterate over that container. For example return a const reference to it or have begin() and end() methods that return (const) iterators accordingly.

Why does C++ have an ostream overloading issue with customised struct vectors?

I have written a code which has to iterate through a vector and print its contents. I am getting an error:
dfs.cpp:45:16: error: no match for ‘operator*’ (operand type is ‘const traits’)
std::cout << *c << ' ';
dfs.cpp:44:15: error: cannot bind ‘std::ostream {aka std::basic_ostream}’ lvalue to ‘std::basic_ostream&&’
std::cout<<*v<
However, the iteration works for a vector of type char
#include<iostream>
#include<list>
#include<vector>
#include<stdio.h>
using namespace std;
struct traits
{
int x;
bool visit;
std::list<int> friends;
};
int main()
{
cout << "Enter the number of employees: " << endl;
int noOfEmployees, noOfFriends;
cin>>noOfEmployees;
std::vector<traits> employees;
int i = 0; int j;
while(noOfEmployees--){
traits v;
v.x = i;
cout << "Enter the no of friends: " << endl;
cin >> noOfFriends;
while(noOfFriends--){
cin>>j;
v.friends.push_back(j);
}
v.visit = false;
employees.push_back(v);
}
std::vector<char> path;
path.push_back('a');
path.push_back('l');
path.push_back('i');
path.push_back('a');
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i){
std::cout << *i << ' ';
}
for(std::vector<traits>::iterator v = employees.begin(); v != employees.end(); ++v){
std::cout<<*v<<endl;
}
}
I have seen few answers but I want to do this without operator overloading, what would be the correct or more C++-nic way?
The error your getting is telling you that the ostream operator doesn't have a rule to apply to objects of type trait. The most "C++nic" way to do this would be to overload the << operator, but you've said that you don't want to do that.
Given that constraint, your can translate the traits item into something the ostream operator supports. A reasonable way to do this would be with a namespace level, non-member function
std::string to_string(const traits& t) {
/// Code to generate a string representation of your traits object
}
for (const auto emp& : employees){
std::cout<< to_string(emp) << ' ';
}
If I saw code like this in production I would expect the author had a reason not to use the more canonical operator overload, and I'd be disappointed if they did not.
When using the iostream operators << & >> for istream and ostream either it be for cout and cin or ifstream, ofstream or fstream objects think of it in the manner of the compiler or one who is writing the compiler...
These are template types. A user will create a template with a various number of types that could be built in or custom user defined. To give this kind of power to the user how would the compiler know if the ostream in your case that will be using a list is a list<int>, list<float>, list<yourType>? So you have to create the overload yourself for all the types that you want to support the operator>>() and operator<<(). And as user Spacemoose beat me to it; you would have to convert the underlying type via stand a lone function to a type that already works with the operators.

Adding string value to map in c++

I am trying to add a char array value into a map, but on displaying the value of char array is not coming, however the integer value is displayed.
That is ii.first is not displayed, however ii.second is displayed correctly.
Here is the complete code which I am running,
#include <iostream>
#include <cstring>
#include <map>
#include <utility>
using namespace std;
class map_demo {
public:
class cmp_str {
public:
bool operator() (char const *a, char const *b) {
return std::strcmp(a, b) <0;
}
};
private:
typedef map <char*, int, cmp_str> ptype;
ptype p;
public:
void set_value() {
char name[20];
int empid;
cout<<"Enter the employee name\n";
cin.getline(name,20);
// cout<<"name entered=:"<<name;
cout<<"Enter the employee id\n";
cin>>empid;
this->p.insert(map<char *,int>::value_type(name,empid));
}
void get_value() {
cout << "Map size: " << p.size() << endl;
for(ptype::iterator ii=p.begin(); ii!=p.end(); ++ii) {
cout <<"the first="<< (*ii).first << ": " << (*ii).second << endl;
}
}
};
//=====================================================================
int main() {
map_demo mp1;
mp1.set_value();
mp1.get_value();
}
The output obtained on running the code:
Enter the employee name
farhan
Enter the employee id
909
Map size: 1
the first=: 909
Here the first = farhan:909, should be the correct output, can anyone make me understand where I am making it wrong??
The problem there as other mentioned is the char *. Also in you case the char * becomes dangling and you are actually pointing to garbage, the reason behind that is that when name goes out of scope that memory is freed, and you are still pointing to that memory, you actually need to copy the data in the map.
this one works
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>
#include <utility>
using namespace std;
class map_demo
{
public:
class cmp_str
{
public:
bool operator() (char const *a, char const *b)
{
return std::strcmp(a, b) <0;
}
};
private:
typedef map <string, int> ptype;
ptype p;
public:
void set_value()
{
char name[20];
std::string inval;
int empid;
cout << "Enter the employee name\n";
cin.getline(name, 20);
inval = name;
//cout<<"name entered=:"<<name;
cout << "Enter the employee id\n";
cin >> empid;
//this->p.insert(map<char *, int>::value_type(name, empid));
this->p.insert(std::pair<string , int>(inval,empid));
}
void get_value()
{
cout << "Map size: " << p.size() << endl;
for (auto ii = p.begin(); ii != p.end(); ++ii)
{
std::string mysf(ii->first);
//std::cout << mysf << std::endl;
cout << "the first=" << mysf << ": " << (*ii).second << endl;
}
}
};
int main()
{
map_demo mp1;
mp1.set_value();
mp1.get_value();
}
Is just a quick fix, probably with a bit more effort can be made better. But just to give you an idea.
If you need to do it with char *, then you probably need to allocate memory yourself in bulk, each time you go and ask for a name you copy that in your data struct and retrieve a pointer to it. To properly handle that the way you make your data struct changes a lot in how clean your result will be, but the core point is, you need to manage your memory, copy in a place which will persist and not get lost, and store a pointer to that memory, not to a region of memory freed when you get out of set_value().
This line
this->p.insert(map<char *,int>::value_type(name,empid));
adds a char* pointer to the map, not the string itself. If the pointer
points to the stack (name[] is on the stack) then it will be the potentially
the same address in each iteration.
Either use a std::string
e.g.
typedef std::map<std::string, int> ptype;
...
p.insert(std::make_pair(name,empid))
or allocate dynamic memory manually and keep track of the string
char* nameStorage = new char[strlen(name)+1];
strcpy(nameStorage,name);
p.insert(std::make_pair(nameStorage,empid));
You defined the key of the map like char *
typedef map <char*, int, cmp_str> ptype;
^^^^^
So in member function set_value
void set_value() {
char name[20];
int empid;
//...
this->p.insert(map<char *,int>::value_type(name,empid));
}
the key is assigned with the address of the local valriable name (more precisely with the address of the first character of the array name) that will be destroyed after exiting the function.
After that the key will be invalied because it will point to a non-existent character array.
Moreover the key shall be copy-assignable but arrays do not have the copy assignment operator.
You could use standard class std::array<char, 20> as the key type. For example
typedef map <std::array<char, 20>, int, cmp_str> ptype;
In this case you have to change also cmp_str that it would accept objects of this type.
Another approach is to use standard class std::string instead of the array. Foir example
typedef map <std::string, int> ptype;

C++: Generic base member functions with specific types in derived class

As will soon become apparent, I'm still learning C++.
For the problem I'm working on, I have a class which has a std::map data member and a number of member functions which operate on the map. I'm going to need some instances for which the map is sorted ascending and some for which the map is sorted descending. The code of the member functions will be identical for both cases (except for type issues--as I've learned, changing the Compare function for a map changes its type). I'm struggling to make this work without having to rewrite the functions for each case.
Here's some toy code which I hope illustrates my situation (inlining functions just for compactness):
class MyClass {
public:
map<int, int>::iterator get_top(){ return data.begin(); }
int sum_elements(){
int sum = 0;
for ( map<int,int>::iterator it = data.begin(); it != data.end(); ++it){
sum += it->second;
}
}
};
Ideally I would have a base class which defines the functions and two derived classes which define the data member, one for ascending and one for descending:
class Ascending : public MyClass {
private:
std::map<int, int, std::less<int> > data;
};
class Descending : public MyClass {
private:
std::map<int, int, std::greater<int> > data;
};
But of course this doesn't work because the base class needs to have the data member defined in order to operate on it.
The "easy" way out would be to have two standalone classes and copy/paste the member functions into both. But that would be wasteful, and I feel in my heart that C++ must have an elegant solution to my problem.
Define a functor that overloads operator()(int, int) (the comparison), and that takes as a constructor the order in which you want to sort the elements. This way, you can define the map
as std::map<int, int, SortCriterion> data(comp), where comp is defined as SortCriterion comp(true) for example for ascending order, and SortCriterion comp(false) for descending. This way, both maps will have the same type, map<int, int, SortingCriterion>.
Code below:
#include <map>
#include <iostream>
using namespace std;
class SortCriterion
{
bool ascending;
public:
SortCriterion(bool sorting_type): ascending(sorting_type){};
bool operator()(int x, int y) const
// if not marked const,
// clang++ spits a compile-time error on insert
// however g++ compiles it ok
{
if(ascending) // ascending
return x<y;
else
return x>y;
}
};
int main()
{
// Both maps below have the SAME type, map<int, int, SortCriterion>
map<int, int, SortCriterion> m1(SortCriterion(true)); // ascending
map<int, int, SortCriterion> m2(SortCriterion(false)); // descending
m1.insert({1,1});
m1.insert({2,2});
m2.insert({1,1});
m2.insert({2,2});
cout << "First map:" << endl;
for(auto elem: m1)
cout << "[" << elem.first << "," << elem.second\
<< "]" << endl;
cout << endl << "Second map:" << endl;
for(auto elem: m2)
cout << "[" << elem.first << "," << elem.second\
<< "]" << endl;
}

How to distinguish read/write operations when using operator[]

I'd need to write a class with an overloaded operator [] which has different behavior when the operator [] is used to read or write data.
To give a practical example of what I want to achieve, let's say I have to write the implementation of a class named PhoneBook which can be used in the following way:
PhoneBook phoneBook(999999); // 999999 is the default number which should be
// used when calling someone who is not in the phone book
phoneBook["Paul"] = 234657; // adds Paul's number
phoneBook["John"] = 340156; // adds John's number
// next line should print Paul's number 234657
cout << "To call Paul dial " << phoneBook["Paul"] << endl;
// next line should print John's number 340156
cout << "To call John dial " << phoneBook["John"] << endl;
// next line should print 999999 because Frank is not in the phone book
cout << "To call Frank dial " << phoneBook["Frank"] << endl;
The problem is in the fact that when using
phoneBook["Frank"]
I don't want to add an entry in the phone book for Frank, otherwise a solution based on std::map would be easy to implement.
I did not find on the web any standard way to achieve this so
after some thinking I came up with the following solution in which the operator [] returns a "temporary object" named PhoneNumber. PhoneNumber is then used to distinguish between read/write operations:
#include <iostream>
#include <string>
#include <map>
using namespace std;
class PhoneBook{
private:
map<string, int> data_; // stores phone numbers
int defaultNumber_; // default number returned when no matching name is found
public:
PhoneBook(int defaultNumber) :
defaultNumber_(defaultNumber) {}
// Searches in the phone book for a name. If the name is found it returns
// the corresponding number. If the name is not found it returns defaultNumber_
int read(string name){
map<string, int>::iterator it = data_.find(name);
if (it==data_.end()){
return defaultNumber_;
} else {
return it->second;
}
}
// Forwarding function to map operator []. It is not really necessary but it is added for clarity
int& write(string name){
return data_[name];
}
// Forward declaration of the "temporary object" returned by operator []
// See declaration below
class PhoneNumber;
PhoneNumber operator[](string name){
return PhoneNumber(this, name);
}
class PhoneNumber{
friend class PhoneBook;
private:
PhoneBook* const phoneBook_;
string name_;
// Constructors are private so that PhoneNumber can be used only by PhoneBook
// Default constructor should not be used
PhoneNumber() :
phoneBook_(NULL) {}
PhoneNumber(PhoneBook* phoneBook, string name) :
phoneBook_(phoneBook), name_(name) {}
public:
// conversion to int for read operations
operator int (){
return phoneBook_->read(name_);
}
// assignment operator for write operations
const int& operator = (const int& val){
return phoneBook_->write(name_) = val;
}
};
};
int main(){
PhoneBook phoneBook(999999);
phoneBook["Paul"] = 234657;
phoneBook["John"] = 340156;
cout << "To call Paul dial " << phoneBook["Paul"] << endl;
cout << "To call John dial " << phoneBook["John"] << endl;
cout << "To call Frank dial " << phoneBook["Frank"] << endl;
return 0;
}
The class PhoneBook behaves like I would like and the program prints:
To call Paul dial 234657
To call John dial 340156
To call Frank dial 999999
I would like to ask you some questions:
Is there any better way to obtain a class behaving like the class I coded?
Has the technique I'm using a name so that I can search more info about it?
Do you see any drawback/possible improvement in my solution?
In the library I'm writing, enabling the behavior I obtained for PhoneBook::operator[]
in a similar situation is really important and I would really like to know what you think about my problem.
Thanks!
What you propose is the standard solution to this problem. It's usually
known as the proxy pattern or proxy idiom, and the helper class that you
return is called a proxy. (Since it is a nested class, simply calling
it Proxy is generally sufficient.)
I think you may implement two versions of operator [], one with const modifier and the other without. Then if you has a object say PhoneBook phoneBook(999999);, the if phoneBook is const object, only operator [] const can be called. If phoneBook is non-const object, default operator [] is called. If you want to call operator [] const given a non-const object, you may add a cast like static_cast<const PhoneBook&>(phoneBook)->operator[...].
#include <iostream>
#include <string>
#include <map>
using namespace std;
class PhoneBook{
private:
map<string, int> data_; // stores phone numbers
int defaultNumber_; // default number returned when no matching name is found
public:
PhoneBook(int defaultNumber) :
defaultNumber_(defaultNumber) {}
int operator [] (const string& name) const
{
map<string, int>::const_iterator it = data_.find(name);
if (it == data_.end())
{
return defaultNumber_;
}
else
{
return it->second;
}
}
int& operator [] (const string& name)
{
return data_[name];
}
};
int main(){
PhoneBook phoneBook(999999);
phoneBook["Paul"] = 234657;
phoneBook["John"] = 340156;
cout << "To call Paul dial " << phoneBook["Paul"] << endl;
cout << "To call John dial " << phoneBook["John"] << endl;
cout << "To call Frank dial " << static_cast<const PhoneBook&>(phoneBook)["Frank"] << endl;
return 0;
}