maps and classes

As an exercise we have been asked to create our own class and use this as the key in a map. So far so good, managed to create a class and put objects in there. However, I get an error when trying to use the map function .find()
The error is

Candidate function not viable: no known conversion from 'const char [6]' to 'const key_type' (aka 'const AddressBook') for 1st argument

Any help is appreciated!!

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <iostream>
#include <map>
#include <string>

#include "AddressBook.hpp"


int main(int argc, const char * argv[]) {
    
    
    std::map<AddressBook, int> info;
    
    info[AddressBook("James","Farrow","Acacia Grove", 11)] = 47;
    info[AddressBook("Ethan","Farrow","Acacia Grove", 11)] = 8;
    info[AddressBook("Ruby","Farrow","Acacia Grove", 11)] = 6;
    info[AddressBook("Sarah","Holdcroft","Acacia Grove", 11)] = 37;
    
    //if(info.find("James") != info.end()){
    //
    //}
    
    
    for(std::map<AddressBook, int>::iterator it = info.begin(); it != info.end();){
        it->first.print();
        std::cout << "Age: " << it->second << std::endl;
        it++;
    }
    
    
    return 0;
}


#ifndef AddressBook_hpp
#define AddressBook_hpp

#include <string>
#include <iostream>

class AddressBook {
public:
    AddressBook();//deafualt constructor for map
    AddressBook(const AddressBook & other);// copy constructor
    AddressBook(std::string, std::string, std::string, int);// constructor
    
    void setFirstName();
    std::string getFirstName() const;
    
    void setSecondName();
    std::string getSecondName() const;
    
    void setAddress();
    std::string getAddress()const;
    
    void setHouseNuber();
    int getHouseNumber() const;
    
    void print() const;
    
    //overload assignment operator for map to order objects
    //overload assignment operator for map to order objects
    bool operator<(const AddressBook &other) const {
        if( secondName == other.secondName){
            return firstName < other.firstName;
        }
        else{
            return firstName < other.firstName;
        }
    }
private:
    int houseNumber;
    std::string firstName;
    std::string secondName;
    std::string address;
    
};

#endif /* AddressBook_hpp */

//this constructor is required for map to be able to construct objects
//if not present program will not compile
AddressBook::AddressBook(): firstName(""), secondName(""), address(""), houseNumber(0){
   
}


//copy constructor
AddressBook::AddressBook(const AddressBook &other){
    //std::cout << "copy" << std::endl;
    firstName = other.firstName;
    secondName = other.secondName;
    address = other.address;
    houseNumber = other.houseNumber;
}

//standard constructor
AddressBook::AddressBook(std::string first, std::string second, std::string addr, int num)
:firstName(first), secondName(second), address(addr), houseNumber(num){
    
}

//print the information;
void AddressBook::print()const{
    std::cout << firstName << " " << secondName << " "
    << houseNumber << " " << address << " ";
}


//set the firstname of object
void AddressBook::setFirstName(){
    std::cout << "First name: ";
    std::getline(std::cin, firstName, '\n');
    
}
std::string AddressBook::getFirstName() const{
    return firstName;
}

void AddressBook::setSecondName(){
    std::cout << "Second name: ";
    std::getline(std::cin, secondName, '\n');
}


std::string AddressBook::getSecondName() const{
    return secondName;
}

void AddressBook::setAddress(){
    std::cout << "Address: ";
    std::getline(std::cin, address, '\n');
}

std::string AddressBook::getAddress() const{
    return address;
}

void AddressBook::setHouseNuber(){
    std::cout << "house Number: ";
    std::cin >> houseNumber;
}

int AddressBook::getHouseNumber()const {
    return houseNumber;
}
remember each element of the std::map is a std::pair<AddressBook, int>
1
2
3
4
5
6
auto itr = std::find_if(info.begin(), info.end(), [](const std::pair<AddressBook, int> a)
                            {return a.first.getFirstName() == "James";});
    //if (itr != info.end())std::cout << itr -> first << "\n";

     if (itr != info.end())itr -> first.print();
    std::cout << "\n";
Last edited on
OP: also checkout this post – it has a very comprehensive list of combining lambdas, algorithms and std::pair/std::map
http://www.cplusplus.com/forum/general/212131/#msg992316
Last edited on
Hi gunnerfunner - thanks very much!

Can you explain it a little to me please πŸ˜‚

James
we're using the lambda to apply a unary predicate to each element of the std::map<AddressBook, int>
as mentioned, in the previous post, each element of the std::map info is a std::pair<AddressBook, int> and the predicate returns true if the first element of the pair's i.e. the AddressBook's getFirstName() method's return value == β€œJames” So adding it all up gives this bit: return a.first.getFirstName() == "James";

finally, one thing I should also mention is the difference between std::find and std::find_if – std::find does not work with a unary predicate overload such as the one you need here to search custom objects: //http://en.cppreference.com/w/cpp/algorithm/find

> we have been asked to create our own class and use this as the key in a map.

Using an address book as the key in a map does not make too much sense, does it?
The typical use of an address book is given a name (key) locate the corresponding address (the mapped value).

Something like this, perhaps:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <map>
#include <string>

struct name
{
    name( std::string first_name,  std::string last_name = {} )
        : first( std::move(first_name) ), last( std::move(last_name) ) {}

    std::string first_name() const { return first ; }

    // ...

    private:
        std::string first ;
        std::string last ;

    friend bool operator < ( const name& a, const name& b )
    {
        using pair = std::pair< const std::string, const std::string > ;
        return pair{ a.first,a.last } < pair{ b.first, b.last } ;
    }

    friend std::ostream& operator << ( std::ostream& stm, const name& n )
    { return stm << n.last << ", " << n.first ; }
};

struct address
{
    address( int house_no, std::string addr, int phone = 0 )
        : house_number(house_no), street_etc( std::move(addr) ), phone(phone) {}
    // ...

    int house_number ;
    std::string street_etc ;
    int phone ;

    // ...
    friend std::ostream& operator << ( std::ostream& stm, const address& a )
    {
        stm << '#' << a.house_number << ", " << a.street_etc ;
        if(a.phone) stm << "  (phone:" << a.phone << ')';
        return stm ;
    }
};

int main()
{
    std::map< name, address > my_address_book
    {
      { {"James","Farrow"}, { 11, "Acacia Grove", 12345} },
      { {"Ethan","Farrow"}, { 11, "Acacia Grove", 12346} },
      { {"Ruby","Farrow"}, { 11, "Acacia Grove" } },
      { {"Sarah","Holdcroft"}, { 11, "Acacia Grove" } },
    };

    // ...

    // look up given the full name (key)
    const auto ethan_farrow = my_address_book.find( {"Ethan","Farrow"} ) ;
    if( ethan_farrow != std::end(my_address_book) )
        std::cout << ethan_farrow->first << " @ " << ethan_farrow->second << '\n' ;

    // look up given the first name (first part of the key)
    // http://en.cppreference.com/w/cpp/container/map/lower_bound
    const auto ruby = my_address_book.lower_bound( { "Ruby" } ) ;
    if( ruby != std::end(my_address_book) && ruby->first.first_name() == "Ruby" )
        std::cout << ruby->first << " @ " << ruby->second << '\n' ;

    // ..
}

http://coliru.stacked-crooked.com/a/aad6e4d14e3f45a6

Note that that instead of using std::map<>::lower_bound(), resorting to the O(N) algorithm std::find_if() would be asinine.

In general, to search for entries in a map, we find that we often are unable to use the maps key_compare and need to provide a different custom predicate, either our design or our understanding of how a map is used is fundamentally flawed.
Last edited on
From β€œThe C++ Standard Library (2nd edition)”, Nicolai Josuttis re the difference b/w std::find and std::find_if:

The if_suffix is used when you can call two forms of an algorithm that have the same number of parameters either by passing a value or by passing a function or function object. In this case the version without the suffix is used for values, and the version with the _if suffix is used for functions and function objects.
Thanks very much chaps - I will look into it.
I appreciate your help!!
Hi JLBorges - yes I agree. It's my first attempt - I'm (hopefully) going to try and refine/improve it.
The execrice was to use a class as the key, and overload the < operator to show how it works. Maybe I have over done it πŸ˜‚
Topic archived. No new replies allowed.