Overloading insertion operator with map

Sep 6, 2013 at 8:22pm
Hi,

This is my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef PHONEBOOK_H
#define	PHONEBOOK_H

#include <map>
#include <string>
#include <utility>
#include <ostream>
#include "Abonent.h"

class PhoneBook {
public:
    void addAbonent(const Abonent& abonent);
    void delAbonent(const Abonent& abonent);
    friend std::ostream& operator<<(std::ostream& stream, const PhoneBook& a);
private:
    std::map<std::string, std::string> base;
    std::map<std::string, std::string>::iterator it;
};

inline std::ostream& operator<<(std::ostream& stream, const PhoneBook& pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}

#endif	/* PHONEBOOK_H */ 


The problem string is:

 
for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {


Output:
error: no match for 'operator=' in 'pb.PhoneBook::it = pb.PhoneBook::base.std::map<_Key, _Tp, _Compare, _Alloc>::begin [with _Key = std::basic_string<char>, _Tp = std::basic_string<char>, _Compare = std::less<std::basic_string<char> >, _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::basic_string<char> > >, std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >]()'


Thank you!
Last edited on Sep 6, 2013 at 8:47pm
Sep 6, 2013 at 8:47pm
I think you need to use a const_iterator since you are dealing with a const PhoneBook. I'd probably make the iterator inside the for loop instead of having it be a member of the class.
Sep 6, 2013 at 8:56pm
Thank you!

> I think you need to use a const_iterator since you are dealing with a const PhoneBook
How I will be increase it? ++(pb.it)

> I'd probably make the iterator inside the for loop instead of having it be a member of the class.
I use the iterator yet in the function:
1
2
3
4
void PhoneBook::delAbonent(const Abonent& abonent) {
    this->it = this->base.find(abonent.getName());
    this->base.erase(it);
}


Maybe (it works):
 
friend std::ostream& operator<<(std::ostream& stream, PhoneBook a);

1
2
3
4
5
6
inline std::ostream& operator<<(std::ostream& stream, PhoneBook pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}
Last edited on Sep 6, 2013 at 9:03pm
Sep 6, 2013 at 9:00pm
But I want to use:
 
const PhoneBook& a


Or must I use?
PhoneBook a

Sorry for my English.
Last edited on Sep 6, 2013 at 9:02pm
Sep 6, 2013 at 9:10pm
> How I will be increase it? ++(pb.it)
Sorry. I can increase it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef PHONEBOOK_H
#define	PHONEBOOK_H

#include <map>
#include <string>
#include <utility>
#include <ostream>
#include "Abonent.h"

class PhoneBook {
public:
    void addAbonent(const Abonent& abonent);
    void delAbonent(const Abonent& abonent);
    friend std::ostream& operator<<(std::ostream& stream, const PhoneBook& a);
private:
    std::map<std::string, std::string> base;
    std::map<std::string, std::string>::const_iterator it;
};

inline std::ostream& operator<<(std::ostream& stream, const PhoneBook& pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}

#endif	/* PHONEBOOK_H */ 


But output is:


error: passing 'const const_iterator {aka const std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >}' as 'this' argument of 'std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >& std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >::operator=(const std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >&)' discards qualifiers [-fpermissive]
Sep 6, 2013 at 9:12pm
But it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class PhoneBook {
public:
    void addAbonent(const Abonent& abonent);
    void delAbonent(const Abonent& abonent);
    friend std::ostream& operator<<(std::ostream& stream, PhoneBook a);
private:
    std::map<std::string, std::string> base;
    std::map<std::string, std::string>::iterator it;
};

inline std::ostream& operator<<(std::ostream& stream, PhoneBook pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}

#endif	/* PHONEBOOK_H */ 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include "PhoneBook.h"
#include "Abonent.h"
using namespace std;

int main(int argc, char** argv) {
    PhoneBook phoneBook;
    Abonent ivanov;
    Abonent petrov;
    Abonent sidorov;
    
    ivanov.setName("Ivanov Ivan");
    ivanov.setNumber("333-333");

    petrov.setName("Petrov Petr");
    petrov.setNumber("999-333");

    sidorov.setName("Sidorov Sird");
    sidorov.setNumber("111-123");
    
    phoneBook.addAbonent(ivanov);
    phoneBook.addAbonent(petrov);
    phoneBook.addAbonent(sidorov);

    cout << phoneBook << endl;
    
    phoneBook.delAbonent(petrov);

    cout << phoneBook << endl;

    return 0;
}


Output:

Ivanov Ivan: 333-333
Petrov Petr: 999-333
Sidorov Sird: 111-123

Ivanov Ivan: 333-333
Sidorov Sird: 111-123
Sep 6, 2013 at 11:28pm
I can see you having an iterator member to serve as a bookmark, but in your operator>> function, you should make a copy of the iterator in the function. Either that, or pass the object by non-const reference instead of value, so you can modify the bookmark of the original object.

Think about it:
The parameter is a reference to a constant object. This means the object cannot (and should not) be modified. In your previous version of the function, you break this rule by modifying the member variable
it
in the loop.
Sep 7, 2013 at 5:43am
>> I can see you having an iterator member to serve as a bookmark
No, I don't need it. I don't use the iterator us a bookmark. It's my mistake. I must use: this->it = base.begin(); in this:

1
2
3
4
5
void PhoneBook::delAbonent(const Abonent& abonent) {
    this->it = base.begin();
    this->it = this->base.find(abonent.getName());
    this->base.erase(it);
}


// The parameter is a reference to a constant object. This means the object cannot (and should not) be modified. In your previous version of the function, you break this rule by modifying the member variable
it
in the loop.

Why the compile don't inform me about the error "the object cannot (and should not) be modified" in this example:
1
2
3
4
5
6
7
/*
void PhoneBook::delAbonent(const Abonent& abonent) {
    this->it = base.begin();
    this->it = this->base.find(abonent.getName());
    this->base.erase(it);
}
*/


I mean this line: this->base.erase(it);

I have two vision of my class:

Vision Number One. It works fine:

PhoneBook.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef PHONEBOOK_H
#define	PHONEBOOK_H

#include <map>
#include <string>
#include <utility>
#include <ostream>
#include "Abonent.h"

class PhoneBook {
public:
    void addAbonent(const Abonent& abonent);
    void delAbonent(const Abonent& abonent);
    friend std::ostream& operator<<(std::ostream& stream, PhoneBook a);
private:
    std::map<std::string, std::string> base;
    std::map<std::string, std::string>::iterator it;
};

inline std::ostream& operator<<(std::ostream& stream, PhoneBook pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}

#endif	/* PHONEBOOK_H */ 


PhoneBook.cpp
1
2
3
4
5
6
7
8
9
10
11
#include "PhoneBook.h"

void PhoneBook::addAbonent(const Abonent& abonent) {
    this->base[abonent.getName()] = abonent.getNumber();
}

void PhoneBook::delAbonent(const Abonent& abonent) {
    this->it = base.begin();
    this->it = this->base.find(abonent.getName());
    this->base.erase(it);
}


main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include "PhoneBook.h"
#include "Abonent.h"
using namespace std;

int main(int argc, char** argv) {
    PhoneBook phoneBook;
    Abonent ivanov;
    Abonent petrov;
    Abonent sidorov;
    
    ivanov.setName("Ivanov Ivan");
    ivanov.setNumber("333-333");

    petrov.setName("Petrov Petr");
    petrov.setNumber("999-333");

    sidorov.setName("Sidorov Sird");
    sidorov.setNumber("111-123");
    
    phoneBook.addAbonent(ivanov);
    phoneBook.addAbonent(petrov);
    phoneBook.addAbonent(sidorov);

    cout << phoneBook << endl;
    
    phoneBook.delAbonent(petrov);

    cout << phoneBook << endl;

    return 0;
}


Output:

Ivanov Ivan: 333-333
Petrov Petr: 999-333
Sidorov Sird: 111-123

Ivanov Ivan: 333-333
Sidorov Sird: 111-123


Vision Number Two. It doesn't work:

I have this line:
 
friend std::ostream& operator<<(std::ostream& stream, const PhoneBook& a);


instead this:
 
friend std::ostream& operator<<(std::ostream& stream, PhoneBook a);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef PHONEBOOK_H
#define	PHONEBOOK_H

#include <map>
#include <string>
#include <utility>
#include <ostream>
#include "Abonent.h"

class PhoneBook {
public:
    void addAbonent(const Abonent& abonent);
    void delAbonent(const Abonent& abonent);
    friend std::ostream& operator<<(std::ostream& stream, const PhoneBook& a);
private:
    std::map<std::string, std::string> base;
    std::map<std::string, std::string>::const_iterator it;
};

inline std::ostream& operator<<(std::ostream& stream, const PhoneBook& pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}

#endif	/* PHONEBOOK_H */ 


PhoneBook.cpp and main.cpp are same.


error: passing 'const const_iterator {aka const 
std::_Rb_tree_const_iterator<std::pair<const 
std::basic_string<char>, std::basic_string<char> > >}' as 
'this' argument of 
'std::_Rb_tree_const_iterator<std::pair<const 
std::basic_string<char>, std::basic_string<char> > >& 
std::_Rb_tree_const_iterator<std::pair<const 
std::basic_string<char>, std::basic_string<char> > 
>::operator=(const 
std::_Rb_tree_const_iterator<std::pair<const 
std::basic_string<char>, std::basic_string<char> > >&)' 
discards qualifiers [-fpermissive]


Thank you!
Last edited on Sep 7, 2013 at 5:45am
Sep 7, 2013 at 6:00am
The important part of that error message is:
passing 'const const_iterator[']... discards qualifiers

Now let's look at the function:
1
2
3
4
5
6
inline std::ostream& operator<<(std::ostream& stream, const PhoneBook& pb) {
    for ((pb.it) = (pb.base.begin()); (pb.it) != (pb.base.end()); ++(pb.it)) {
        stream << pb.it->first << ": " << pb.it->second << std::endl;
    }
    return stream;
}


Your parameter is a constant object. Constants are constant; they cannot be modified, and any member of a constant object should also be constant (or read only for functions).
Now look at your for loop:
 
for((pb.it) = (pb.base.begin());/*...*/; ++(pb.it))

Here you modify a member of pb. But pb is constant, meaning pb.it, too, should be constant.
That's what the error means by "discards [const] qualifier".

Considering your operator>>() function, you should just create a new iterator instead of attempting to modify pb.it.
1
2
3
4
5
6
7
8
9
10
inline std::ostream& operator<<(std::ostream& stream, const PhoneBook& pb) {
    for (std::map<std::string, std::string>::const_iterator iter = (pb.base.begin()); iter != (pb.base.end()); ++iter) {
        stream << iter->first << ": " << iter->second << std::endl;
    }
//There is also the C++11 auto keyword redefinition, which would let you write
//for (auto iter = (pb.base.begin()); iter != (pb.base.end()); ++iter) {
//The type should be the same because begin() members return a
//   const_iterator when called with a const object
    return stream;
}
Last edited on Sep 7, 2013 at 6:04am
Sep 7, 2013 at 6:41am
You solved my problem! Now I understand it. Thank you very much :)
Sep 7, 2013 at 6:48am

//There is also the C++11 auto keyword redefinition, which would let you write
//for (auto iter = (pb.base.begin()); iter != (pb.base.end()); ++iter) {
//The type should be the same because begin() members return a
// const_iterator when called with a const object


I read about it in the book "Professional C++," 2 edition. I like this book. Thank you!
Last edited on Sep 7, 2013 at 6:49am
Topic archived. No new replies allowed.