What is difference between const and non const key? - c++

What is the difference between the following two lines?
map<int, float> map_data;
map<const int, float> map_data;

int and const int are two distinct types.
std::map<int, float> and std::map<const int, float> are, similarly, different types.
The difference between std::map<const int, float> and std::map<int, float> is, to a degree, analogous to the difference between, say, std::map<int, float> and std::map<std::string, float>; you get a fresh map type for each.
In the non-const case, the internal key type is still non-const int:
std::map<const int, float>::key_type => const int
std::map<int, float>::key_type => int
However, map keys are semantically immutable, and all map operations that allow direct access to keys (for example, dereferencing iterators, which yields value_type) does constify the key_type:
std::map<const int, float>::value_type => std::pair<const int, float>
std::map<int, float>::value_type => std::pair<const int, float>
So the difference may be largely invisible to you in every way that matters, if your implementation allows it.
That's not always the case, though: the standard officially requires your key type to be copyable and moveable, and some implementations re-use map nodes; under those implementations, attempting to use a const key simply won't work.

The key is already const, so it is redundant to write const in this case. Once an element is entered, its key cannot be changed.
Edit:
As mentioned in the comments, there is difference between the two lines. For example, if you write a function that accepts map<const int, int>, you can't pass to it map<int, int> since they're different types.
But note that although they are different types, they behave the same since the key in a map is a const anyway...
So in conclusion.. The only difference is that they are two different types, you shouldn't care about anything else.

The difference is that the second variant will set the key type for the map as const int. From the "modifiability" point of view this is redundant, since the map already stores its keys as const objects.
However, this can also lead to unexpected and non-obvious differences in the behavior of these two maps. In C++ a template specialization written for type T is different from specialization written for type const T. That means the above two versions of the map might end up using different specializations of various "satellite" templates that depend on the key type. One example is the key comparator predicate. The first one will use std::less<int> while the second one will use std::less<const int>. By exploiting this difference you can easily make these maps to sort their elements in different order.
Issues like that are more obvious with the new C++11 containers like std::unordered_map. std::unordered_map<const int, int> will not even compile, since it will attempt to use a std::hash<const int> specialization for hashing the keys. Such specialization does not exist in the standard library.

const can't be altered once set. And yes as per docs & other answer you should remember that key is const already.
Link: http://www.cplusplus.com/reference/map/map/
Link: http://en.cppreference.com/w/cpp/container/map

While the behaviour of your application will typically be the same, it makes a difference to some compilers you might use. The more specific example of what brought me to this page in the first place:
Explicitly specifying a map as map<const key, value> builds successfully with the gnu toolkit;
However it crashes a Studio12 Solaris x86 build.
map<key, value> builds successfully on both. Behaviour of the application is unchanged.

Const keys can be helpful if the keys are pointers. Using const keys won't let you modify the pointed object when accessing the keys, consider this:
#include <map>
#include <string>
int glob = 10;
int main() {
std::map<const int*, std::string> constKeyMap { { &glob, "foo"} };
std::map<int*, std::string> keyMap { { &glob, "bar" } };
for(const auto& kv : keyMap) { *(kv.first) = 20; }; // glob = 20
for(const auto& kv : constKeyMap) { *(kv.first) = 20; }; // COMPILE ERROR
return 0;
}

const refers to a constant, that, once defined, can't be altered then... non const key is subjected to change... or cant even change, it's just that "no change" is guaranteed in const (once defined), and "change" may or may not occur in non const stuff.

Related

Why do C++ std container iterators ignore some template parameters of their underlying container?

Here is a specific example. In the code below, I would have expected a compilation error like "cannot assign value of type std::map<int, int, cmp>::iterator to variable of type std::map<int, int>::iterator". However, I am able to compile the code without any issues (g++ C++20 Wall). Thanks for help.
#include <map>
struct cmp {
bool operator()(const int a, const int b) const { return a > b; }
};
int main(int argc, char *argv[]) {
std::map<int, int> m1;
std::map<int, int>::iterator m1_it = m1.begin();
std::map<int, int, cmp> m2;
std::map<int, int, cmp>::iterator m2_it = m2.begin();
// Why do these work?
m1_it = m2.begin();
m2_it = m1.begin();
return 0;
}
Standard sets few constraints on iterator types and just imposes some behaviours.
In particular:
they don't have to be in namespace std. So T* might be a valid iterator for std::vector<T>.
they don't have to have same template parameters than their container. For above example T* might be an iterator for std::vector<T, Allocator> for any Allocator.
Whereas, indeed, it is less type safe, less template parameter allows less instantiations (faster compilation, less generated similar code, ...).
Notice that your code might not compile for compilers/libraries which uses all template parameters for their iterators.
One practical reason is that this might lead to smaller executables. Your two map types can share one iterator type, so all the iterator functions are also shared.

Casting from pair<K,V>& to pair<const K,V>& [duplicate]

So I have a smart iterator that emulates a map const_iterator, and it needs to build the return type internally. Obviously, I'd like to store a pair<Key, Value> in my iterator class (since I need to modify it), but at the same time I'd like the dereference functions to present a pair<const Key, Value> (actually it would be a const pair<const Key, Value>& and const pair<const Key, Value>* respectively). The only solution I've come up with so far is to dynamically allocate a new pair every time change the value that my iterator class points to changes. Needless to say, this is not a good solution.
I've also tried *const_cast<const pair<const Key, Value> >(&value) where value is declared as pair<Key, Value>.
Any help would be greatly appreciated (as would the knowledge that it can't be done).
EDIT
For the curious: I ended up storing a pair<const Key, Value> p in my iterator class. In order to change the pair I alter the two elements separately based on the underlying iterator (map<Key, Value>::const_iterator it), const_casting the key so that it could be altered, like this:
*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;
Not a solution I'm terribly happy with, but it gets the job done, and the dereference methods are happy because I'm storing something of the correct type, which they can refer to.
You can convert a value of type pair<Key,Value> to pair<const Key,Value>.
However, reading the question carefully, you're actually asking if, given a pair<Key,Value> you can create a pointer or reference to pair<const Key,Value> referring to the same object.
The answer is no - the only situation where a reference or pointer to one type can refer to an object of a different type is if the object type inherits from the referenced type.
One possibility is to return a pair of references, pair<const Key&, Value&>, created from the pair you wish to reference.
Yes.
std::pair<int, double> p(1,2);
std::pair<const int, double> q = p; // no problem
//q.first = 8; // error
q.second = 9;
int b; double d;
std::pair<int &, double &> s(b,d);
std::pair<int const &, double &> t = s; // also fine
As Kerrek SB pointed out, you can construct std::pair<const Key, Value> from std::pair<Key, Value>. However, your original question implies that you want to avoid constructing std::pair objects each time your iterator is dereferenced.
Unfortunately, there is not a good way to do this. You may have to construct the pair object and actually store it somewhere, particular for operator->. Otherwise you have to be able to have your map actually store pair<const Key, Value> to be able to return references/pointers to it from your iterator. Basically to return a reference/pointer, it has to be stored somewhere in that form: it cannot be a temporary.
Avoid the const_cast. That's just asking for undefined behavior when you use it to cast pair this way even though it may work quite often.
I met exactly the same problem. My solution was to create a new map object as part of the iterator class, then add to it members that are missed in the upstream map class and return the reference to members of it. Not very efficient, but works.
Your solution have two issues:
Assigning a const variable with const_cast is undefined behavior. The compiler optimisations could provide strange results.
Any new dereference would invalidate the results of previous dereference. It should not to. So, depending of the usage of your iterator it may also produce strange results.
Corrected by #antonpp, this is an undefined behaviour that shouldn't be recommended any more.
This can be solved by reinterpret_cast, and it is purely a compile-time directive.
std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1); // type of p2 is pair<const int, double>&
//++(p2.first); // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);
However, reinterpret_cast is a dangerous behaviour. I would suggest adding an static assert, to prevent unexpected partial template specialization.
using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;
static_assert(
offsetof(srcType, first) == offsetof(tarType, first)
&& offsetof(srcType, second) == offsetof(tarType, second)
);

Compare function for pair

I have a pair: typdef pair <unsigned char *, vector<int>> pair_t
I need to implement my own comparison function for that map, so I tried :
struct myCmp
{
int operator()(unsigned char arr_1[10], unsigned char arr_2[10])
{
return memcmp(arr_1, arr_2, 10);
}
};
typdef pair <unsigned char *, vector<int>, **myCmp**> pair_t;
pair_t data(someCharArr, someIntVector);
The error message I get is:
wrong number of template argument (3 should be 2)
I did the same thing with map and it was fine.
How can create my own compare function for pair?
How can I be sure that the pair_t data(someCharArr, someIntVector); will find the correct key (in case of char * as key)?
Thanks.
You seem to be confused about responsibilities of different classes. It's not the std::pair that needs to worry about how to compare pairs together, it's the std::map who needs to worry about how to sort its keys.
typedef std::map<unsigned char*, std::vector<int>, myCmp> map_t;
From how you implement the comparison function and your comment on why you can't use strings, I actually recommend using a std::array instead of a unsigned char *. Your code then looks more like:
typedef std::map<std::array<unsigned char, 10>, std::vector<int> > map_t;
This works because std::array implements all the comparison operators allowing you to use them as keys in a map.
This is going off of the fact that you seem to know the length of your array at compile time, and that it is always 10. If you don't actually know the length of your arrays at compile time, then you would use a std::vector<unsigned char> instead of the std::array.
std::pair is just a struct with two members, It have no comparison object built in.
You may be confused for a comparison object given for std::map (which in its turn, holds sequence of std::pair).

Is it possible to cast a pair<Key, Value> to a pair<const Key, Value>?

So I have a smart iterator that emulates a map const_iterator, and it needs to build the return type internally. Obviously, I'd like to store a pair<Key, Value> in my iterator class (since I need to modify it), but at the same time I'd like the dereference functions to present a pair<const Key, Value> (actually it would be a const pair<const Key, Value>& and const pair<const Key, Value>* respectively). The only solution I've come up with so far is to dynamically allocate a new pair every time change the value that my iterator class points to changes. Needless to say, this is not a good solution.
I've also tried *const_cast<const pair<const Key, Value> >(&value) where value is declared as pair<Key, Value>.
Any help would be greatly appreciated (as would the knowledge that it can't be done).
EDIT
For the curious: I ended up storing a pair<const Key, Value> p in my iterator class. In order to change the pair I alter the two elements separately based on the underlying iterator (map<Key, Value>::const_iterator it), const_casting the key so that it could be altered, like this:
*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;
Not a solution I'm terribly happy with, but it gets the job done, and the dereference methods are happy because I'm storing something of the correct type, which they can refer to.
You can convert a value of type pair<Key,Value> to pair<const Key,Value>.
However, reading the question carefully, you're actually asking if, given a pair<Key,Value> you can create a pointer or reference to pair<const Key,Value> referring to the same object.
The answer is no - the only situation where a reference or pointer to one type can refer to an object of a different type is if the object type inherits from the referenced type.
One possibility is to return a pair of references, pair<const Key&, Value&>, created from the pair you wish to reference.
Yes.
std::pair<int, double> p(1,2);
std::pair<const int, double> q = p; // no problem
//q.first = 8; // error
q.second = 9;
int b; double d;
std::pair<int &, double &> s(b,d);
std::pair<int const &, double &> t = s; // also fine
As Kerrek SB pointed out, you can construct std::pair<const Key, Value> from std::pair<Key, Value>. However, your original question implies that you want to avoid constructing std::pair objects each time your iterator is dereferenced.
Unfortunately, there is not a good way to do this. You may have to construct the pair object and actually store it somewhere, particular for operator->. Otherwise you have to be able to have your map actually store pair<const Key, Value> to be able to return references/pointers to it from your iterator. Basically to return a reference/pointer, it has to be stored somewhere in that form: it cannot be a temporary.
Avoid the const_cast. That's just asking for undefined behavior when you use it to cast pair this way even though it may work quite often.
I met exactly the same problem. My solution was to create a new map object as part of the iterator class, then add to it members that are missed in the upstream map class and return the reference to members of it. Not very efficient, but works.
Your solution have two issues:
Assigning a const variable with const_cast is undefined behavior. The compiler optimisations could provide strange results.
Any new dereference would invalidate the results of previous dereference. It should not to. So, depending of the usage of your iterator it may also produce strange results.
Corrected by #antonpp, this is an undefined behaviour that shouldn't be recommended any more.
This can be solved by reinterpret_cast, and it is purely a compile-time directive.
std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1); // type of p2 is pair<const int, double>&
//++(p2.first); // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);
However, reinterpret_cast is a dangerous behaviour. I would suggest adding an static assert, to prevent unexpected partial template specialization.
using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;
static_assert(
offsetof(srcType, first) == offsetof(tarType, first)
&& offsetof(srcType, second) == offsetof(tarType, second)
);

Cast vector<T> to vector<const T>

I have a member variable of type vector<T> (where is T is a custom class, but it could be int as well.)
I have a function from which I want to return a pointer to this vector, but I don't want the caller to be able to change the vector or it's items. So I want the return type to be const vector<const T>*
None of the casting methods I tried worked. The compiler keeps complaining that T is not compatible with const T.
Here's some code that demonstrates the gist of what I'm trying to do;
vector<int> a;
const vector<const int>* b = (const vector<const int>* ) (&a);
This code doesn't compile for me.
Thanks in advance!
If you have a const vector<int> you cannot modify the container, nor can you modify any of the elements in the container. You don't need a const vector<const int> to achieve those semantics.
On why a vector<T> cannot be correctly converted to a vector<const T> even if T can be converted to const T
This is a common recurring problem in programming whether it is with constness or inheritance (a container of derived object cannot be converted to a container of base objects, even if the contained elements themselves can). The problem is that element by element each one of them can be converted, but the container itself cannot without breaking the type system.
If you were allowed to do vector< const T > &vr = my_vector_of_T, then you would be allowed to add elements through vr, and those elements would be constant by definition. But at the same time those same elements would be aliased in my_vector_of_T as non-const elements and could be modified through that interface, breaking constness in the typesystem.
In the particular case of a vector<int> being converted to a vector<const int>, chances are that you would not notice really weird effects --besides adding an element to a vector<const int> and seeing how the constant element changes in time, but still remember that given two related types T1 and T2 for which a relation exists, in most cases trying to apply the same relationship to containers of T1 and T2 will break the type system.
In addition to James's answer about how to do it you should note that const int is not a valid type to put into any standard container since it is not assignable.
you can force conversion like this:
b = reinterpret_cast<const std::vector<const int>*>(&a);
but I do not think you should do this, since it is not guaranteed to work, only to compile
The compiler decides to block this. However, we know this is safe so maybe we can fool it:
const vector<const int>* b = (const vector<const int>* )(void *)(&a);