Luke Peterson
Luke Peterson

Reputation: 981

subscript operators for class with std::map member variable

I am trying to make a class that wraps std::map and does checking to make sure the keys are one the of approved valid strings, and also initializes the map to have default values for all the approved valid strings. I am having issues getting the subscript operator to work, specifically the const version of it.

Here is my class prototyping code:

#include <set>
#include <string>
#include <map>

class foo {
  public:
    foo() {}
    const double & operator[](const std::string key) const {
      return data[key];
    }
  private:
    static const std::set<std::string> validkeys;
    std::map<std::string, double> data;
};

const std::set<std::string> foo::validkeys = {"foo1", "foo2"};

When I compile this (using g++ with -std=c++0x), I get this compilation error:

|| /home/luke/tmp/testmap.cc: In member function 'double& foo::operator[](std::string) const':
testmap.cc|10 col 22 error| passing 'const std::map<std::basic_string<char>, double>' as
'this' argument of 'mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const
key_type&) [with _Key = std::basic_string<char>, _Tp = double, _Compare =
std::less<std::basic_string<char> >, _Alloc = std::allocator<std::pair<const 
std::basic_string<char>, double> >, mapped_type = double, key_type = 
std::basic_string<char>]' discards qualifiers

Nothing I do seems to fix this. I have tried

I don't know if I'm even approaching this problem correctly so if there is some other simple way to create a class that allows this kind of functionality:

foo a;
a["foo2"] = a["foo1"] = 5.0;
// This would raise a std::runtime_error because I would be checking that
// "foo3" isn't in validkeys
a["foo3"] = 4.0;

Any suggestions greatly appreciated.

SOLUTION

The following works exactly how I want it to, I even have a basic exception when you try to set or get a key that isn't in the set of valid keys:

#include <iostream>
#include <string>
#include <map>
#include <set>
#include <stdexcept>

class myfooexception : public std::runtime_error
{
  public:
    myfooexception(const std::string & s)
      : std::runtime_error(s + " is not a valid key.") {}
};

class foo {
  public:
    foo() {
     for (std::set<std::string>::iterator it = validkeys.begin();
          it != validkeys.end();
          ++it) {
       data[*it] = 0.0;
     }
    }
    const double & operator[](const std::string & key) const {
      if (data.find(key) == data.end()) {
        throw myfooexception(key);
      } else {
        return data.find(key)->second;
      }
    }
    double & operator[](const std::string & key) {
      if (data.find(key) == data.end()) {
        throw myfooexception(key);
      } else {
        return data[key];
      }
    }
  private:
    static const std::set<std::string> validkeys;
    std::map<std::string, double> data;
};

const std::set<std::string> foo::validkeys = {"foo1", "foo2"};

int main(void)
{
  foo a;
  a["foo1"] = 2.0;
  a["foo1"] = a["foo2"] = 1.5;
  // a["foo3"] = 2.3; // raises exception:  foo3 is is not a valid key
  const foo b;
  std::cout << b["foo1"]; // should be ok
  // b["foo1"] = 5.0;  // compliation error, as expected: b is const.

  return 0;
}

Upvotes: 2

Views: 2777

Answers (4)

Andrei Bozantan
Andrei Bozantan

Reputation: 3921

The operator [] is not declared const in the std::map, because the operator [] also inserts a new element when the key is not found and returns a reference to its mapped value. You can use the map::find method instead of map::operator[] if you want your operator[] to be const.

Upvotes: 3

Simon Richter
Simon Richter

Reputation: 29588

The subscript operator for std::map is non-const as it inserts a new element if one does not yet exist. If you want your map to have a const operator[], you need to write one that uses map::find() and tests against map::end(), handling the error case.

Upvotes: 2

ken
ken

Reputation: 836

you are trying to modify a const object!! please remove the const of set.const members cannot be modified once they are initialised.

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409176

You are trying to assign to the std::map but your function is declared const and also returning const. Remove both const and it should work.

Upvotes: 0

Related Questions