C++ map access discards qualifiers (const) - c++

The following code says that passing the map as const into the operator[] method discards qualifiers:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class MapWrapper {
public:
const int &get_value(const int &key) const {
return _map[key];
}
private:
map<int, int> _map;
};
int main() {
MapWrapper mw;
cout << mw.get_value(42) << endl;
return 0;
}
Is this because of the possible allocation that occurs on the map access? Can no functions with map accesses be declared const?
MapWrapper.cpp:10: error: passing const std::map<int, int, std::less<int>,
std::allocator<std::pair<const int, int> > > as this argument of
_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&)
[with _Key = int, _Tp = int, _Compare = std::less<int>,
_Alloc = std::allocator<std::pair<const int, int> >] discards qualifiers

std::map's operator [] is not declared as const, and cannot be due to its behavior:
T& operator[] (const Key& key)
Returns a reference to the value that is mapped to a key equivalent to key, performing insertion if such key does not already exist.
As a result, your function cannot be declared const, and use the map's operator[].
std::map's find() function allows you to look up a key without modifying the map.
find() returns an iterator, or const_iterator to an std::pair containing both the key (.first) and the value (.second).
In C++11, you could also use at() for std::map. If element doesn't exist the function throws a std::out_of_range exception, in contrast to operator [].

Since operator[] does not have a const-qualified overload, it cannot be safely used in a const-qualified function. This is probably because the current overload was built with the goal of both returning and setting key values.
Instead, you can use:
VALUE = map.find(KEY)->second;
or, in C++11, you can use the at() operator:
VALUE = map.at(KEY);

You cannot use operator[] on a map that is const as that method is not const as it allows you to modify the map (you can assign to _map[key]). Try using the find method instead.

Some newer versions of the GCC headers (4.1 and 4.2 on my machine) have non-standard member functions map::at() which are declared const and throw std::out_of_range if the key is not in the map.
const mapped_type& at(const key_type& __k) const
From a reference in the function's comment, it appears that this has been suggested as a new member function in the standard library.

First, you should not be using symbols beginning with _ because they are reserved to the language implementation/compiler writer. It would be very easy for _map to be a syntax error on someone's compiler, and you would have no one to blame but yourself.
If you want to use an underscore, put it at the end, not the beginning. You probably made this mistake because you saw some Microsoft code doing it. Remember, they write their own compiler, so they may be able to get away with it. Even so, it's a bad idea.
the operator [] not only returns a reference, it actually creates the entry in the map. So you aren't just getting a mapping, if there is none, you are creating one. That's not what you intended.

Related

reinterpret_cast adding const

Is it possible to add a const qualifier to only one member of a struct with reinterpret_cast.
#include <utility>
std::pair<int, int> test;
std::pair<const int, int>& ctest = reinterpret_cast<std::pair<const int, int>&>(test);
ctest.second = 5;
int first = ctest.first;
I only want to add const, I don't care about removing it. Google does this in their implementation of BTree. I'm also implementing a BTree so I wonder if this is even legal.
Your code has undefined behavior due to violation of type aliasing rules.

boost::bind with maps, what's the difference between binding std::pair and std::map::value_type?

What's the difference between the following two cases?
std::pair<int,std::string> example_1 (std::make_pair (1,"foo"));
int value_1 = boost::bind (&std::pair<int,std::string>::first,_1) (example_1);
std::map<int,std::string>::value_type example_2 (std::make_pair (2,"boo"));
int value_2 = boost::bind (&std::pair<int,std::string>::first,_1) (example_2);
The first example works fine but the second does not compile when the binding is done. I have looked at the file stl_map.h and value_type is defined as follows:
typedef std::pair<const _Key, _Tp> value_type;
I don't see the difference. I would appreciate is someone can tell let me know and the reason why the second example does not compile.
The compilation error message is:
.../include/boost/bind/mem_fn.hpp:333:36: error: no matching function for call to ‘get_pointer(const std::pair<const int, std::basic_string<char> >&)’
make: *** [main.o] Error 1
Thanks in advance!
The difference is the use of const in the map value type. std::pair<int,std::string>::first is not an accessible item on std::pair<const int,std::string>::first. Yes, there's an implicit conversion defined by pair from the const version to the non-const version, but that conversion isn't considered for the purposes of calling a member function like this. Those uses of pair might look similar, but really they are completely independent classes that are unrelated to each other in terms of where field locations and such might go.
In essence, you'e asked boost to build code like
std::pair<const int,std::string> example(3, "Hello");
example.std::pair<int,std::string>::first
which is not valid.
map's value_type has a const key (const int in your case), whereas the pair you're using doesn't (plain int in your case).

Use of C++ templates with dynamic binding class

In the past I have used both templates and dynamic binding in C++, however recently I attempted to use them together and found that it was impossible to compile.
What I am trying to do is something like this:
std::map<MyClass, unsigned int> mymap;
Where MyClass happens to be a class utilizing dynamic memory binding. After a few hours of searching I am given the impression that this causes implications which I still can't resolve, so I was hoping for some guidance on the issue and how it can be resolved.
My final goal is to be able to do something like this:
std::vector<MyClass> MyClassPool;
//fill the vector with MyClass objects...
for(usigned int i=0 ; i<MyClassPool.size() ; i++)
{
mymap[ MyClassPool[i] ] = i;
}
I have tried using pointers in various ways but it's not working and I fail to see what can be done.
I get the following errors with the above:
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h: In member function `bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Instance]':
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_map.h:338: instantiated from `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = Instance, _Tp = float, _Compare = std::less<Instance>, _Alloc = std::allocator<std::pair<const Instance, float> >]'
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h:227: error: no match for 'operator<' in '__x < __y'
That compile error means you have no operator < defined for Instance. map needs to be able to sort keys and needs this function. If you'd rather not define operator <, you need to provide a comparison function as a third template parameter for map, i.e. std::map<Instance, float, compare_instances>.
... Come to think of it, you're sure you want Instance as the key and float as the data, and not the other way around? I.e. you're searching in the map for an Instance to get a float in return?
You don't provide operator< for MyClass. It is required by std::map. You have two options: provide a comparator as the third template argument to map OR implement the operator in MyClass.
This has nothing to do with "dynamic binding" (which is not what is meant here anyway). Your class needs to have an order to be put into a map. It needs operator<.

Why is it useful to make a const function?

why is it so useful to make a function const if you only can read variables but not write(class variable)?
If you pass something else a const pointer or const reference to an instance of your class then it can only call the class's const methods (if any).
Obviously, if you never bother with const-correctness with your types then you can ignore this.
I suppose it may also help the compiler optimize things in certain situations, although I am doubtful and, even if it did help, allowing that small improvement (if any) to dictate how you wrote your code would be a case of premature optimization in most situations.
So that you do not "accidentally" modify one of the class variables. It is just a safety measure.
(If you use the const keyword after a function that does modify any data member of the class - either directly or through another function call - you will get a compilation error).
One reason is that const is a virus. That means that if part of the code is const-correct, then the rest of the code won't interoperate with that part.
If you ignore const-correctness, chances of your classes working hand-in-hand with other libraries (beginning with the standard library) are slim.
For example:
#include <vector>
#include <algorithm>
struct X
{
int n;
bool operator< (X b)
{
return n < b.n;
}
};
int main()
{
std::vector<X> vec;
std::sort(vec.begin(), vec.end());
}
With codepad.org
In function 'const _Tp& std::__median(const _Tp&, const _Tp&, const _Tp&) [with _Tp = X]':
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_algo.h:2642: instantiated from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<X*, __gnu_norm::vector<X, std::allocator<X> > >, __gnu_debug_def::vector<X, std::allocator<X> > >, _Size = int]'
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_algo.h:2713: instantiated from 'void std::sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<X*, __gnu_norm::vector<X, std::allocator<X> > >, __gnu_debug_def::vector<X, std::allocator<X> > >]'
t.cpp:17: instantiated from here
Line 90: error: no match for 'operator<' in '__a < __b'
A stdlib compatible comparison operator must make a promise that arguments are not modified. If objects actually were to change while they are compared, an attempt to sort them would be rather futile.
Another example: you won't be able to pass arguments by const reference which is the conventional way of passing large objects. Instead you'd have to pass arguments by modifiable references. Now you won't be able to pass temporaries of any kind to your functions.
If you have a const object, it only allows const member functions to operate on it.
The more of the contract between caller and callee you can express in code instead of documentation, the more help the compiler can give you with complying with that contract (from both ends). const-ness of the implicit this pointer is an important part of that as is const-ness of all other parameter referents. (of course top-level constness of pass-by-value parameters is not part of the contract)
The benefit is that you can get the compiler to enforce where state can be modified. For example if you make a class with private data and all its methods, except, say, the constructor, are const, then you have an immutable data type. The benefit of this is not one of performance, but one of semantics.

Type requirements for std::map

Today I created a map, where the value type has no default constructor. I was surprised that I could not use operator[] to insert the elements to this map, but I had to use the insert method.
So, what exactly are requirements for the key and value types for std::map?
Here is short example :
#include <map>
struct A
{
A(int){}
};
int main()
{
std::map< int, A > m;
A a1(2);
A a2(3);
A a3(4);
m[5] = a1;
m[3] = a2;
m[2] = a3;
}
I am compiling like this :
[vladimir#sandbox tmp]$ g++ b5.cpp -Wall -Wextra -ansi -pedantic
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stl_map.h: In member function ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = A, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, A> >]’:
b5.cpp:14: instantiated from here
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stl_map.h:419: error: no matching function for call to ‘A::A()’
b5.cpp:5: note: candidates are: A::A(int)
b5.cpp:4: note: A::A(const A&)
operator[] does indeed require default-constructibility because the semantics of this method mandate that if the key doesn’t yet exist, an appropriate entry is created. Thus:
map<TKey, TValue> mymap;
TKey foo = …;
TValue& x = mymap[foo];
will create and store a new object TValue() if foo doesn’t exist in the map, and return a reference to it.
This site make a great STL reference: http://www.sgi.com/tech/stl/
Basically, it says that map takes has mandatory 2 type arguments, Key and Data. Data needs to be Assignable, as Daniel said. Key, however, is stated to need to be a type that can be used with the type Compare, i.e. Compare designates a function object whose parameters are of type Key. In this case, the default Compare function object is std::less<T>, which is a Strict Weak Ordering that compares objects of type T using the operator<. Hence, if you do not change the Compare type, i.e. use the default, std::less<T> will be used with type Key, and thus operator< will be used with type Key, and thus Key needs to be comparable with operator<.
Hope that helps! I know it's a bit gratuitous and I don't mean to be condescending, but I just want to make sure it's absolutely clear how to go about reasoning about this.