Home > Back-end >  Const correctness with a std::map
Const correctness with a std::map

Time:12-04

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
}

CodePudding user response:

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.

CodePudding user response:

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 beggining, anything is modifiable, so the answer to you question is that they are exactly the same. There is no diferences.

CodePudding user response:

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

Very seldomly: Any. You would usually keep your map confined in a class and only expose it via member functions.

What are the tradeoffs or differences between constMap and moreConstMap?

It depends on what constMap and moreConstMap really means. They are not standardized. Standard consts may make it possible for the optimizing part of the compiler to make assumptions to exclude certain branches in code that would actually be able to change the variable. Not needing to deal with those branches may make a program a bit faster.

CodePudding user response:

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.

  • Related