468,272 Members | 2,040 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,272 developers. It's quick & easy.

unable to read from const std::map<>&

63
Actually I'm quite sure I've missed something trivial here, but I just can't find it.
Seemingly I cannot read from a const map&

I try

Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <map>
  3. using namespace std;
  4.  
  5. void show ( 
  6.   const // troubling-const
  7.   map<char, double>& myconstmap )
  8. {
  9.   cout << "A->" << myconstmap['A'] << "\n";
  10. }
  11.  
  12. int main ()
  13. {
  14.   map<char, double> mymap;
  15.   mymap['A']=0.1;
  16. }
  17.  
  18.  
If I remove the troubling const I'm able to access it but I don'r really feel like giving up on const

I'm thankful for any hint
J
Nov 29 '07 #1
12 5494
weaknessforcats
9,207 Expert Mod 8TB
The const has nothing to do with it.

It's your use of operator[] in the the cout in line 9. That operator returns a reference to the map element. It's possible to change the map byusing this reference. Your const gets in the way of that and you get a compile error.

Either: a) don't use const or b) use const but don't use the map [] operator.
Nov 29 '07 #2
jabbah
63
The const has nothing to do with it.

It's your use of operator[] in the the cout in line 9. That operator returns a reference to the map element. It's possible to change the map byusing this reference. Your const gets in the way of that and you get a compile error.
ok

Either: a) don't use const
mhh, I dont really feel like this is a solution, but rather a workaround which I dont feel comfortable with. The map is created in one spot and I want to hand it around and read it from multiple places without the possibility to change it.


or b) use const but don't use the map [] operator.
But as far as I can see the only other way to access the elements in the map then is the const_iterator which is not suitable for me - I rather would need some kind of random access.

Well, ok thanks for your answer.
Maybe map is just not the right thing here - I'll look for something else.

Thanks.
Nov 29 '07 #3
weaknessforcats
9,207 Expert Mod 8TB
Why insist on map::operator[] ???

You can use map::find() instead. That will return the pair that has your key and value.

Otherwise, you can write a new map iof your own be deriving from map (bs sure map has a virtual destructor) and then write an operator[] in your map that returns a const value for the key rather than a const_iterator.

Please note, access in a map is not random, like an array. Access is based on a key and not and element number. The map::operator[] tries to make the map look like an array but it does return the value in a form that allows you to change it.
Nov 29 '07 #4
jabbah
63
Why insist on map::operator[] ???

You can use map::find() instead. That will return the pair that has your key and value.
Because I dont want to do something in logarithmic time, when it should be done in constant time.

Otherwise, you can write a new map iof your own be deriving from map (bs sure map has a virtual destructor) and then write an operator[] in your map that returns a const value for the key rather than a const_iterator.
That sounds like a very interessting idea - I think I'll try that. Very interessting. I have never thought about deriving from some stl thing. mhh...


Please note, access in a map is not random, like an array. Access is based on a key and not and element number. The map::operator[] tries to make the map look like an array but it does return the value in a form that allows you to change it.
I don't get that. What I meant when I said random was that I can access an arbitrary element in constant time - independent of whether I'm allowed to change the element or not. And as I expect a map to be something like a hashtable and I know my keys will all be unique (Im not actually using char as indicated in my example but pointers to struct), I would expect [] to execute in constant time.
Although now that you made me think about it again I dont find any statement about execution time of [] in
http://www.cppreference.com/cppmap/map_operators.html
Nov 29 '07 #5
weaknessforcats
9,207 Expert Mod 8TB
What I meant when I said random was that I can access an arbitrary element in constant time - independent of whether I'm allowed to change the element or not. And as I expect a map to be something like a hashtable and I know my keys will all be unique (Im not actually using char as indicated in my example but pointers to struct), I would expect [] to execute in constant time.
A map is a red-black binary tree. Access is not in constant time like an array.

All STL associative containers use red-black trees.

There is a hashmap but it is still non-standard.
Nov 30 '07 #6
jabbah
63
A map is a red-black binary tree. Access is not in constant time like an array.
Ok, I see. So the execution times of both, operator[] and find() are logarithmic, right?

I think, I'm going to put the map into a wrapper that provides something like
Expand|Select|Wrap|Line Numbers
  1. TYPE& operator[]( const key_type& key );
  2. const TYPE& operator[]( const key_type& key ) const;
Besides the satisfaction of my const-request this would allow me to change from map to whatever, maybe a hash table in the future.
Dec 3 '07 #7
weaknessforcats
9,207 Expert Mod 8TB
Yes, you could change the implementation of the tree.

Generally, I advise to not expose your implementation for that very reason. All of these STL containers should be fronted by an access class of your design and with your interface methods.

Ditto for memory using new and delete. I always use a Create/Delete function and bury the allocation.
Dec 3 '07 #8
jabbah
63
Thanks weaknessforcats
Dec 3 '07 #9
jabbah
63
Errr, Im afraid Im back .... I tried to:

you can write a new map iof your own be deriving from map (bs sure map has a virtual destructor) and then write an operator[] in your map that returns a const value for the key rather than a const_iterator.
like this:

Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <map>
  3. using namespace std;
  4.  
  5.  
  6. template < typename keytype, typename TYPE >
  7. class cmap: public std::map<keytype, TYPE> 
  8. {
  9. public:
  10.   virtual const TYPE& operator[](const keytype& k) const // this seems to keep me from accessing the non-const operator[]
  11.   //virtual const TYPE& get(const keytype& k) const 
  12.   {
  13.     // get rid of const so we are able to call operator[]()
  14.     const cmap<keytype,TYPE>& const_cmap = *this;
  15.     const std::map<keytype,TYPE>& const_map = const_cmap;
  16.     std::map<keytype,TYPE>& nonconst_map = const_cast < std::map<keytype,TYPE>& > ( const_map );
  17.     // retrieve the element and return it as const&
  18.     return nonconst_map[k];
  19.   }
  20.  
  21.   virtual ~cmap(){}
  22. };
  23.  
  24.  
  25.  
  26. int main (){
  27.  
  28.   cmap <char, double> mycmap;
  29.   mycmap['A'] = 0.2; // compile-error: "assignment of read-only location"
  30.  
  31.   const cmap<char, double>& const_mycmap = mycmap;
  32.   cout << const_mycmap['A'] << "\n";
  33.   //cout << const_mycmap.get('A') << "\n";
  34. }
This doesnt compile due to line 29, so seemingly I do have a const operator[] now, but the non-const operator[] is gone.

If I exchange line 15 with 16 and 37 with 38 it works, but well then there is still no const operator[]() const.
Dec 6 '07 #10
weaknessforcats
9,207 Expert Mod 8TB
You have to write your own cmap::operator[]. One for non-const cmaps and one for const cmaps.

No amount casting is going to help you.

Here is my try. It compiles and links but I didn't test it.
Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <map>
  3. #include <exception>
  4. using namespace std;
  5.  
  6.  
  7. template < typename keytype, typename TYPE >
  8. class cmap: public std::map<keytype, TYPE> 
  9. {
  10. public:
  11.     TYPE& operator[](const keytype& k)
  12.  
  13.   {
  14.       map::iterator theData = this->lower_bound(k);
  15.       if (theData == this->end())
  16.       {
  17.            exception obj("key not found");
  18.            throw obj;
  19.  
  20.  
  21.       }
  22.       return theData->second;
  23.   }
  24.   TYPE operator[](const keytype& k) const
  25.  
  26.   {
  27.       map::const_iterator theData = this->lower_bound(k);
  28.       if (theData == this->end())
  29.       {
  30.            exception obj("key not found");
  31.            throw obj;
  32.  
  33.  
  34.       }
  35.       return theData->second;
  36.   }
  37.   virtual ~cmap(){}
  38. };
  39.  
  40.  
  41.  
  42. int main (){
  43.  
  44.   cmap <char, double> mycmap;
  45.   mycmap['A'] = 0.2; // compile-error: "assignment of read-only location"
  46.  
  47.   const cmap<char, double>& const_mycmap = mycmap;
  48.   cout << const_mycmap['A'] << "\n";
  49.   //cout << const_mycmap.get('A') << "\n";
  50. }
  51.  
Lastly, the camp destructor does not need to be virtual unless you create a cmap and use it as a map* or map&. And if you do that, be sure the map destructor is virtual. If it's not, that the signal that map cannot be used polymorphically.
Dec 6 '07 #11
jabbah
63
You have to write your own cmap::operator[]. One for non-const cmaps and one for const cmaps.
ok, I had never guessed that!

And - ok, learned something again - obviously it is not possible to return a const TYPE& if there is no such object. which finally is the answer to my unspelled question "why is the const TYPE& operator[]() const not already implemented in std::map?". But the exception is a nice resolution to this. I think it suits my needs quite well, since if my code ever tries to call const_mycmap[key] for some undefined key, it is a bug anyways and I'll be thankful for the exception.

But contrary to your proposal, I don't want an exception in the case of accessing an undefined key in the non-const context. E.g. line 45 of your code raises an exception. Also it changes the semantics of the base class std::map in a surprising way.

Additionally, I needed to change the declaration of the iterators and replace the exception object. But this might be subject to different compilers.

Below is the version that works for me and with which I feel quite satisfied.

Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <map>
  3. #include <exception>
  4. using namespace std;
  5.  
  6.  
  7. class message: public exception 
  8. {
  9. protected:
  10.   string What;
  11. public:
  12.   message( string m ): What(m) {}
  13.   ~message() throw() {}
  14.   virtual const char* what() const throw() {
  15.     return What.c_str();
  16.   }
  17. };
  18.  
  19.  
  20.  
  21. template < typename keytype, typename TYPE >
  22. class cmap: public std::map<keytype, TYPE> 
  23. {
  24. public:
  25.  
  26.   TYPE& operator[](const keytype& k) 
  27.   {
  28.     // use the operator[] of the base class map
  29.     return map<keytype,TYPE>::operator[](k);
  30.   }
  31.  
  32.  
  33.   const TYPE& operator[](const keytype& k) const 
  34.   {
  35.     //map::const_iterator theData ; // g++ says: 
  36.     // `template<class _Key, class _Tp, class _Compare, class _Alloc> 
  37.     // class std::map' used without template parameters
  38.     // missing template arguments before "theData"
  39.  
  40.     typename map< keytype, TYPE >::const_iterator theData ; 
  41.     // took me a while to figure out that (for whatever reason) typename 
  42.     // is required here
  43.  
  44.     theData = this->lower_bound(k);
  45.     if ( theData == this->end() )
  46.       {
  47.         //exception obj("key not found"); // g++ says: 
  48.         // no matching function for call to 
  49.         // `std::exception::exception(const char[14])'
  50.  
  51.         message obj("key not found");
  52.         throw obj;
  53.       }
  54.     return theData->second;
  55.   }
  56.  
  57.   virtual ~cmap(){}
  58. };
  59.  
  60.  
  61.  
  62. int main ()
  63. {
  64.  
  65.   try 
  66.     {
  67.       cmap <char, double> mycmap;
  68.       mycmap['A'] = 0.2; 
  69.       cout << mycmap['A'] << " {" << &(mycmap['A']) << "}\n";
  70.  
  71.       const cmap<char, double>& const_mycmap = mycmap;
  72.       cout << const_mycmap['A'] << " {" << &(const_mycmap['A']) << "}\n";
  73.       cout << const_mycmap['B'] << "\n"; // raises an exception, which is good!
  74.     }
  75.   catch ( message e )
  76.     {
  77.       cout << "Exception: \"" << e.what() << "\"\n";
  78.       exit( EXIT_FAILURE );
  79.     }
  80. }
  81.  
Dec 6 '07 #12
jabbah
63
bugfix: it should be

theData = this->find(k);

instead of

theData = this->lower_bound(k);
Feb 28 '08 #13

Post your reply

Sign in to post your reply or Sign up for a free account.

Similar topics

5 posts views Thread by Mark Van Orman | last post: by
6 posts views Thread by scottyman | last post: by
3 posts views Thread by markoj | last post: by
reply views Thread by subramanian100in | last post: by
reply views Thread by zattat | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.