stl unordered multimap no match for operator

I'm trying to use the STL unordered multimap. I read a file in and insert it into a vector, then strip the puncuation, then insert the words into the map. I then want to have the program spit out where in the map a certain key is. (it can be in multiple places)

My problem is that the compiler throws this error:

error: no match for ‘operator<’ (operand types are ‘std::pair<const std::__cxx11::basic_string<char>, int>’ and ‘const std::__cxx11::basic_string<char>’)
{ return *__it < __val; }
~~~~~~^~~~~~~

Does anyone have any ideas why the compiler is throwing this error? I declared the pair correctly according the cppreference, so I don't know why the operator is throwing an error.

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
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <utility>

using namespace std;

typedef unsigned int uint;
typedef unordered_multimap<string, int> hashtable;

template <typename T>
void replace_punctuation(T p, T p_end){
	while(p != p_end){
		if(ispunct(*p) && *p != 95){
			*p = 32;
		}
		p++;
	}
}


int main(int argc, char* argv[]){
    bool verbose = false;
	string fname = "";

	if(argc < 3 || argc > 4){
		cout << "Usage: ./hash1 -f [file] -verbose(optional)" << endl;
		return -1;
	}
	
	for(int i = 1; i < argc; i++){
		string option = argv[i];
		if(option.compare(0,2, "-f") == 0){
			fname = argv[i+1];
		}

		else if(option.compare("-verbose") == 0){
			verbose = true;
		}
	}


    hashtable map;
    ifstream fin(fname);
    vector<string>cache;
    string line, word;
    int line_num = 1;
    while(fin >> line){
        cache.push_back(line);
        replace_punctuation(line.begin(), line.end());
        stringstream ss(line);
        while(ss >> word){
            //insert into multimap.
            
            map.insert(make_pair(word, line_num));
            line_num = line_num + 1;
        }
    }

    fin.close();

    if(verbose){
        cout << map.bucket_count() << endl;
        cout << map.load_factor() << endl;
    }


    pair<hashtable::iterator, hashtable::iterator> range;
    hashtable::iterator start_it;
    hashtable::iterator end_it;

    while(!cin.eof()){
        cout << "find>";
        
        string word;
        cin >> word;

        range = equal_range(map.begin(), map.end(), word);
        start_it = range.first;
        end_it = range.second;

        while(start_it != end_it){
            
            cout << start_it->first;

            start_it++;
        }
    }
}
The problem is the call to std::equal_range.
That function uses the < operator between elements of the map.
https://en.cppreference.com/w/cpp/algorithm/equal_range
partitioned with respect to element < value or comp(element, value) (that is, all elements for which the expression is true precedes all elements for which the expression is false)


I don't think you need to be using std::equal_range. I think you can get away with just using a foreach loop.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <utility>

using namespace std;

typedef unsigned int uint;
typedef unordered_multimap<string, int> hashtable;

int main(){

    hashtable map;
    
    std::string word = "hello";
    int line_num = 42;

    map.insert(make_pair(word, line_num));
    
    for (const std::pair<std::string, int>& element : map) // (or just do const auto&)
    {
        std::cout << element.first << " " << element.second << '\n';
    }
}


Other notes:

You do not need to use the int values of characters, you can use the character symbol itself.
- 95 is an underscore, so you can do *p != '_'
- 32 is a space, so you can do *p = ' '

In general, avoid using eof() or similar conditions for looping.
You want to check if the input itself was successfully extracted, so you want to check to see if line 79 was successful.
1
2
3
4
5
6
7
8
if (cin >> word)
{
    // good
}
else
{
    // unable to extract
}

Last edited on
@Ganado Thank you, I really appreciate it
 
range = equal_range(map.begin(), map.end(), word);


Use the map specific version of equal_range - not the algorithm one.

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
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <algorithm>

using hashtable = std::unordered_multimap<std::string, int>;

template <typename T>
void replace_punctuation(T p, const T p_end) {
    for (; p != p_end; ++p)
        if (*p != '_' && std::ispunct(static_cast<unsigned char>(*p)))
            *p = ' ';
}

int main(int argc, char* argv[]) {
    bool verbose {};
    std::string fname;

    if (argc < 3 || argc > 4)
        return (std::cout << "Usage: ./hash1 -f [file] -verbose(optional)\n"), -1;

    for (int i {1}; i < argc; ++i) {
        const std::string option {argv[i]};

        if (option.compare(0, 2, "-f") == 0)
            fname = argv[i + 1];
        else if (option == "-verbose")
            verbose = true;
    }

    std::ifstream fin(fname);

    if (!fin)
        return (std::cout << "Cannot open file\n"), -2;

    std::vector<std::string>cache;
    hashtable map;
    int line_num {};

    for (std::string line; fin >> line; ) {
        cache.push_back(line);
        replace_punctuation(line.begin(), line.end());

        std::stringstream ss(line);

        for (std::string word; ss >> word; )
            map.emplace(word, ++line_num);
    }

    if (verbose) {
        std::cout << map.bucket_count() << '\n';
        std::cout << map.load_factor() << '\n';
    }

    for (std::string word; std::cout << "find> " && (std::cin >> word); ) {
        const auto range {map.equal_range(word)};

        for (auto start_it = range.first; start_it != range.second; ++start_it)
            std::cout << start_it->first << '\n';
    }
}

Last edited on
Topic archived. No new replies allowed.