44

I want to use a pair from STL as a key of a map.

#include <iostream>
#include <map>

using namespace std;

int main() {

typedef pair<char*, int> Key;
typedef map< Key , char*> Mapa;

Key p1 ("Apple", 45);
Key p2 ("Berry", 20);

Mapa mapa;

mapa.insert(p1, "Manzana");
mapa.insert(p2, "Arandano");

return 0;

}

But the compiler throw a bunch of unreadable information and I'm very new to C and C++.

How can I use a pair as a key in a map? And in general How can I use any kind of structure (objects, structs, etc) as a key in a map?

Thanks!

Soo Wei Tan
  • 3,262
  • 2
  • 34
  • 36
ccarpenterg
  • 779
  • 2
  • 10
  • 11
  • 7
    In the future, please post the error messages you get. Otherwise, it can often be difficult or impossible for people to help you. :) – James McNellis Jul 18 '10 at 20:46
  • 3
    If you do post the errors, I'm sure we could also help explain what they mean and how you can interpret them when you see them in the future. – James McNellis Jul 18 '10 at 21:02
  • 1
    Note that, using string literals, the _addresses_ of the strings are compared, not the strings themselves. You'd better use `std::string`. – sbi Jul 18 '10 at 22:37
  • Also have a look at [Why can't I compile an unordered_map with a pair as key?](https://stackoverflow.com/q/32685540/3679900) – y2k-shubham Sep 03 '20 at 11:21

6 Answers6

35

std::map::insert takes a single argument: the key-value pair, so you would need to use:

mapa.insert(std::make_pair(p1, "Manzana"));

You should use std::string instead of C strings in your types. As it is now, you will likely not get the results you expect because looking up values in the map will be done by comparing pointers, not by comparing strings.

If you really want to use C strings (which, again, you shouldn't), then you need to use const char* instead of char* in your types.

And in general How can I use any kind of structure (objects, structs, etc) as a key in a map?

You need to overload operator< for the key type or use a custom comparator.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 2
    `mapa[p1] = "Manzana";` is even shorter – Peter G. Jul 18 '10 at 21:13
  • 4
    @Peter: `operator[]` has different semantics, and I'd recommend against using it for inserting objects into a `map` (it inserts a new object if one does not already exist, then overwrites the newly created temporary object immediately). – James McNellis Jul 18 '10 at 21:18
  • Wow That was an ugly mistake, I forgot to make the pair. I'm sorry! Well it works now but it didn't work when I was using char* instead of const char*. What's the deal with const char* vs char* in this case? Thanks! – ccarpenterg Jul 18 '10 at 21:20
  • 1
    @ccarpenterg: No need to apologize to me. :-) The string literals have a type `const char[]`, the pair type has a member of type `char*`; the pair constructor cannot remove the const qualifier. That said; really: use `std::string`, using a pointer as a map key is almost always A Bad Idea. – James McNellis Jul 18 '10 at 21:23
  • @JamesMcNellis can you update this to modern C++? I had wondered that my `std::map` worked without problems using a `std::pair` until I noticed that [since C++14 it has `operator<` defined](https://en.cppreference.com/w/cpp/utility/pair/operator_cmp). – andreee May 24 '19 at 13:30
8

Here's a working rewrite of the code in question:

#include <map>
#include <string>

class Key
{
  public: 
    Key(std::string s, int i)
    {
      this->s = s;
      this->i = i;
    }
    std::string s;
    int i;
    bool operator<(const Key& k) const
    {
      int s_cmp = this->s.compare(k.s);
      if(s_cmp == 0)
      {
        return this->i < k.i;
      }
      return s_cmp < 0;
    }
};

int main()
{


  Key p1 ("Apple", 45);
  Key p2 ("Berry", 20);

  std::map<Key,std::string> mapa;

  mapa[p1] = "Manzana";
  mapa[p2] = "Arandano";

  printf("mapa[%s,%d] --> %s\n",
    p1.s.c_str(),p1.i,mapa.begin()->second.c_str());
  printf("mapa[%s,%d] --> %s\n",
    p2.s.c_str(),p2.i,(++mapa.begin())->second.c_str());

  return 0;
}
Alec Jacobson
  • 6,032
  • 5
  • 51
  • 88
5

Alternatively to what James McNellis stated:

mapa.insert(std::make_pair(p1, "Manzana"));

you could use mapa.insert({p1, "Manzana"});

Delimitry
  • 2,987
  • 4
  • 30
  • 39
Julian Declercq
  • 1,536
  • 3
  • 17
  • 32
1

This is a similar version of what you want to do, just change the data types, that's all. Also, use a c++ string, not the one we use in c.

#include<bits/stdc++.h>
using namespace std;
#define  ll long long int
typedef pair<ll,ll> my_key_type;
typedef map<my_key_type,ll> my_map_type;
int  main()
{
    my_map_type m;
    m.insert(make_pair(my_key_type(30,40),6));
}   
Pavel Smirnov
  • 4,611
  • 3
  • 18
  • 28
0

std::map::emplace is your friend.

mapa.emplace(p1, "Manzana");
Shishir Jaiswal
  • 135
  • 3
  • 6
-1

This is will do exactly what you want

#include<bits/stdc++.h>
using namespace std;
int main()
{
    map<pair<string, long long int>, string> MAP;
    pair<string, long long int> P;
    MAP.insert(pair<pair<string, long long int>, string>(pair<string, long long int>("Apple", 45), "Manzana"));
    MAP.insert(pair<pair<string, long long int>, string>(pair<string, long long int>("Berry", 20), "Arandano"));
    P = make_pair("Berry", 20);
    //to find berry, 20
    cout<<MAP[P]<<"\n";
    return 0;
}
trahul
  • 21
  • 4