Reputation: 981
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
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
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
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
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