Panda
Panda

Reputation: 967

Const correctness with a std::map

How much const should you apply to a std::map?

// Given a custom structure, really any type though
struct Foo
{
   int data;
};

// What's the difference between these declarations?
const std::map<int, Foo> constMap;
const std::map<const int, const Foo> moreConstMap;

What are the tradeoffs or differences between constMap and moreConstMap?

The answer may apply to other stl containers too.

Edit1.

To provide some more context. One potential use case to consider the difference between the two might be a static lookup table scoped to a .cpp file. Let's say...

//Foo.cpp
namespace {
   const std::map<int, Foo> constConfigMap{ {1, Foo{1}}, {2, Foo{2}} };
   // vs
   const std::map<const int, const Foo> moreConstConfigMap{ {1, Foo{1}}, {2, Foo{2}} };
}

void someFunctionDefinition()
{
   Foo blah { constConfigMap.at(2) };

   // do something with blah
}

Upvotes: 2

Views: 1678

Answers (3)

Bilal Aabkari
Bilal Aabkari

Reputation: 94

std::map contains pair's of {key, value}. key is always constant and you can not modify it. On the other hand, value is modifiable, and if you use const, it's not. Because of those const at the beginning, anything is modifiable, so the answer to you question is that they are exactly the same. There is no differences.

Upvotes: 1

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14589

There is no difference in case of std::map aside of them being distinct types.

  • As variables are declared const, the state of map class itself cannot be changed.

  • Only member functions you can call upon it are const-qualified member functions, e.g. const_iterator find( const Key& key ) const;

Both const-qualified find() will return a const_iterator which would be referring an object of const std::pair<const int, Foo> type (just as operator[] would) in case of constConfigMap and const std::pair<const int, const Foo> in case of moreConstConfigMap. Those types are nearly synonyms because second member of std::pair isn't declared as mutable. Those are different types though because they are declared by different set of lexemes, which can be illustrated by following code:

struct Foo
{
   int data;
   void bar() {}
   // needs to be const-qualified
   bool operator==(const Foo& other) const { return data == other.data; }
};

namespace {
   const std::map<int, Foo> constConfigMap{ {1, Foo{1}}, {2, Foo{2}} };
   // vs
   const std::map<const int, const Foo> moreConstConfigMap{ {1, Foo{1}}, {2, Foo{2}} };
}

int main() {
    auto it1 = constConfigMap.find(1);
    auto it2 = moreConstConfigMap.find(1);
    *it1 == *it2; //  error: no match for ‘operator==’
    it1->second == it2->second;
    it1->second.bar();  // error: passing ‘const Foo’ as ‘this’ argument discards qualifiers

    // neither is ill-formed, what would happen is other question
    const_cast<Foo&>(it1->second).bar(); 
    const_cast<Foo&>(it2->second).bar();
    return 0;
}

Compiler would point out that those are different types.

      |     *it1 == *it2;
      |     ~~~~ ^~ ~~~~
      |     |       |
      |     |       pair<[...],const Foo>
      |     pair<[...],Foo>

You can't change either map or its elements at all. You can't call non-const-qualified members upon an instance of Foo without discarding const.

Upvotes: 2

Sneftel
Sneftel

Reputation: 41474

As has already been mentioned, keys in a map are implicitly const.

Making the value a const type will prevent you from using mymap[k] = v, because the index operator will return a reference to const. You’ll need to use insert/emplace and erase to modify the map.

Upvotes: 1

Related Questions