Unordered Map - get value with const key - c++

I have an unordered map that uses a pointer to a custom object as key.
For some reason looking up the values with the key only works if the key is not const.
Here is an example (with std::string as stand-in for the custom object):
std::unordered_map<std::string*, int> my_map;
std::string key {"test"};
const std::string const_key {"test2"};
auto value = my_map.at(&key); // this works as expected
auto other_value = my_map.at(&const_key); // this doesn't compile
error: invalid conversion from 'const string* {aka const std::__cxx11::basic_string<char>*}'
to 'std::unordered_map<std::__cxx11::basic_string<char>*, int>::key_type
{aka std::__cxx11::basic_string<char>*}' [-fpermissive]
Why would a lookup require the pointer to be non const?

When you write &const_key this evaluates to const std::string *, but your map uses std::string * as key type.
There is a difference between "address to string" and "address to a string that is const". Therefore these are considered different types, and you cannot use them interchangeably.
P.S. Could not write this as comment, so posted as answer.

thought I would offer a little more reasoning as to why this is going on (I ran into the same problem... grumble grumble grumble).
Consider the following code:
template <typename T>
void f(T t) { *t += 1; }
int main() {
const int x = 0;
//f<int*>(&x); //error: no matching function for call to ‘f<int*>(const int*)’
//f(&x); //error: assignment of read-only location ‘* t’
int y = 0;
f<int*>(&y); //okay, what we're "supposed" to do
int * const z_p = &y;
f(z_p); //okay, we'll copy z_p by value and modify y
}
In this (somewhat minimal) example, we can clearly see why we can't take a const int * as an argument to our function f(int *). If we did, we could modify it (not good). This covers the no matching function call, but in the second case we get through the template deduction with no problems, but hit a wall when we try to modify our value (that's not the template functions fault, you are using it wrong!) The third case is boring and expected, and I threw in the fourth case just to remind those of you who, like me, get confused with funny pointer types.
If you are sure of that the function you are calling won't modify your thing, or you are an international man of mystery, then there is always the option to cast your pointer.
f<int*>((int*)&x); //that's just lazy
f<int*>(const_cast<int*>(&x)); //that's just crazy
In this specific example, the results of the code above are undefined. I ran it on my machine with g++ --std=c++14 -g -O0 -Wall and got about what I expected, the value of x didn't change a bit. Today we were playing around with this situation and found that because this is undefined behavior, the compiler is allowed to optimize out the const value read from the object code. Note that x still exists on the stack, and you can modify that place in memory like you would any other, but when you read it the initial value will likely just be given by the compiler. Finally, if you move the definition of x to global scope, then you may very well have a segfault if you cast away the const-ness and modify the location in memory.
In general, I find the confusion somewhat justified, because the std::unordered_map<Key,T>::value_type is std::pair<const Key, T> (https://en.cppreference.com/w/cpp/container/unordered_map). In my mind I sort of thought "oh, then I can just shove a const whatever in there and it'll all work out." Then I found this post and scratched my head for a second, thought of the above example, and once again found that the language is protecting me from my funny self. Sigh...
For more reading on the matter, see: https://en.cppreference.com/w/cpp/language/template_argument_deduction.

The map is declared like
std::unordered_map<std::string*, int> my_map;
^^^^^^^^^^^^
You are truing to call the method at with a key of type const std::string *
auto other_value = my_map.at(&const_key);
^^^^^^^^^^
There is no implicit conversion from the type const std::string * to the type std::string *.
You could for example declare the map like
std::unordered_map<const std::string *, int> my_map;
and in this case both arguments std::string * and const std::string * could be used.

Related

Given std::map with pointers to a non-constant class for keys, how to access it via a pointer to const?

There is a map of a type std::map<A*, B*> m that describes correspondence between objects of type A and objects of type B.
There is a function int exctractInfo(const A *a) that needs to read some info from an object of type B which corresponds to a given object of type A. This is a semantically constant operation, nothing needs to be changed, we just need to read some info, but the problem is that C++ doesn't allow access to map m via a pointer to const.
Consider the following code:
#include <map>
class A {
};
class B {
int info_;
public:
int info() const { return info_; }
};
std::map<A*, B*> m;
int exctractInfo(const A *a) {
auto it = m.find(a);
if (it != m.end() && it->second) {
return it->second->info();
}
return -1;
}
int main () {
return 0;
}
Here's a link to online compiler for this code. I get the following error:
error: invalid conversion from 'const A*' to 'std::map::key_type {aka A*}' [-fpermissive]
Now I see two solutions:
Rewrite the type std::map<A*, B*> as std::map<const A*, B*>, as I have access to the source code, but that is basically a type of a library object, and a lot of code depend on it, so it will have to be changed, thus changing the map type is really undesirable;
Use const cast like this: auto it = m.find(const_cast<A*>(a));, which also doesn't seem like a good solution, more of a hack.
I don't understand why it doesn't work. If the key is std::string or int, for example, I can access std::map<std::string, B*> via const std::string just fine. So what's wrong with my example? Is there a proper way to handle this situation?
I don't understand why it doesn't work. If the key is std::string or int, for example, I can access std::map via const std::string just fine. So what's wrong with my example?
Because there is a significant difference btw constant pointer to non constant data and non constant pointer to constant data. Your map has first as a key, you try to pass second. So if you are pre C++14 only viable solution would be const_cast I am afraid (beside changing key type of course). If you can use C++14 or later then "transparent comparison" is available as stated in std::map::find() example. For it to work you would need to declare your map like this:
std::map<A*, B*,std::less<>> m;
live example

boost::any confusion with pointers vs values

It took me a while to figure this out, but the semantics of boost::any are confusing.
With value types, you use it like so:
int value = 100;
boost::any something;
something = value;
//...later...
int value = boost::any_cast<int>(&something);
This code is clear and makes sense, but stores value internally as a copy. This means for larger objects I place inside boost::any, they will be copied. Also any functions that I replace void* with this will expect that the value outside the function is modified when I modify the value contained in the boost::any object (which won't happen, since it copied it).
So if I put pointers into it, things get weird:
int value = 100;
boost::any something;
something = &value;
//...later...
int* value = *boost::any_cast<int*>(&something);
I have to dereference the return value in this case because boost::any_cast returns int**! I also haven't checked but I think this may crash if something.empty() == true. This is just not straightforward at all.
I do not want to store values in my boost::any, I want it to only function on pointers and behave semantically closer to void*. Pointers in, pointers out, with some type-safety mixed in. Essentially what I want is boost::any_pointer, or something like that. Is there a way to prohibit boost::any from accepting anything except pointers? And if not, is there an alternative to boost::any that can give me the semantics I'm looking for?
You are using any_cast wrong:
There are essentially two (three) flavors.
Taking a reference to any and returning a value or reference to the content
Taking a pointer to any and returning a pointer to the content
Examples:
#include <boost/any.hpp>
int main()
{
// Any holding a value
{
boost::any any_value(1);
// Throws bad_any_cast if the content is not 'int' (in this case):
int value = boost::any_cast<int>(any_value);
// Throws bad_any_cast if the content is not 'int' (in this case):
int& reference = boost::any_cast<int&>(any_value);
// Returns a null pointer if the content is not 'int' (in this case):
int* pointer = boost::any_cast<int>(&any_value);
}
// Any holding a pointer (which is nothing else but a value)
{
int integer = 0;
boost::any any_ptr(&integer);
// Throws bad_any_cast if the content is not 'int*' (in this case):
int * pointer = boost::any_cast<int*>(any_ptr);
// Throws bad_any_cast if the content is not 'int*' (in this case):
int*& pointer_reference = boost::any_cast<int*&>(any_ptr);
// Returns a null pointer if the content is not 'int*' (in this case):
int** pointer_pointer = boost::any_cast<int*>(&any_ptr);
}
}
See also: http://en.cppreference.com/w/cpp/experimental/any and http://en.cppreference.com/w/cpp/experimental/any/any_cast
NOTE: I am assuming you want to store pointers in boost::any, since you are looking for something on the lines of boost::any_pointer (though non-existent).
boost::any_cast returns the pointer to the actual value stored inside it (the held object inside holder). So it kind of saves a copy unless you actually want to copy it later.
template<typename ValueType>
ValueType * any_cast(any * operand) BOOST_NOEXCEPT
{
return operand && operand->type() == boost::typeindex::type_id<ValueType>()
? &static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
: 0;
}
You can always create a wrapper around boost::any to allow only pointer types:
class MyAny {
public:
template <typename T,
typename = typename std::enable_if<std::is_pointer<std::remove_cv<T>::type>::value>::type>
MyAny(T ptr): any_(ptr) {}
template <typename ValueType>
ValueType operator*() {
return *boost::any_cast<ValueType>(&any_);
}
private:
boost::any any_
};
Above is a rough code, I have not compiled and tested it.
std::enable_if type trait is available in C++11, but can be found in boost as well. It is what that will constrain your MyAny to only pointer types.
Although this is quite an old question, I nevertheless would like to point out a simple misconception [I think] in the preface of the asked questions:
int value = 100;
boost::any something;
something = &value; // 1)
//...later...
int* value = *boost::any_cast<int*>(&something); // 2)
1) This saved "int *" in something.
2) This contains essentially several steps:
"&something" gets the address of something, i.e. return "boost::any *"
"boost::any_cast()" now casts this address to a pointer to an int - which is probably not what void.pointer wanted. And I don't know whether - and if, why - this seems to work.
And then you dereference "*boost::..." this, i.e. cancelling the "&" from the first step again.
What I think, void.pointer wanted to do here, is more along the following line:
int* value = boost::any_cast<int*>(something);
.
PS: I would have liked to put this in a simple comment and not a full-fledged answer - stackoverflow however requires me to collect enough points first, so...

Not understanding C++ type mismatch: const Foo* to Foo* const&

Having this set of objects and statements:
QSet<Foo*> set;
iterator QSet::insert(const T & value) //type of the function I want to call
const Foo * get() const //type of the function I use to get the argument
set.insert(get()); //the line showing up as error
I get the error "no known conversion for argument 1 from 'const Foo*' to 'Foo* const&". I guess I have trouble reading these types because I have no idea what I should do to make this work.
From what I've read, the const keyword applies to the type to its left with the exception of a top-level const which can be written to the left of the type it applies to. My guess would be that I have to convert get() to a reference but I'm unsure how to do that.
There seem to be a couple of misunderstandings here (both by the questioner and by some answers).
First, you said "My guess would be that I have to convert get() to a reference but I'm unsure how to do that". Let's try clearing this up:
1) "I have to convert get() to a reference" -- Actually, you don't!
iterator QSet::insert(const T & value) does indeed take a reference. But it's a reference to type T. So the question is, "what is type T"?
In this case, T=Foo *. So insert, in this instance of the template, is actually:
iterator QSet::insert(Foo * const & value) -- read it from right to left: insert takes a reference to a constant pointer to a Foo.
2) "I'm unsure how to do that [convert a pointer to a reference]" -- while you don't have to do it here, in general you do this by de-referencing the result of get. For example: *(get()).
Second, the compiler error. The error arises because there is a conflict:
a) get() returns a const Foo *
b) but set stores a Foo* -- NOT a const Foo *, so insert only accepts a changeable Foo
So you can't store a constant pointer inside your QSet<Foo*>. This makes sense because you can use set to access and change the Foos inside it, which you promise not to do with a const Foo.
This reference should be helpful:
https://isocpp.org/wiki/faq/const-correctness
You may also think whether you can just use a QSet<Foo> instead of a QSet<Foo*>. In the former case, things will probably behave how you expect.
You are trying to take a const Foo * and insert it into a QSet<Foo *>, but the compiler won't automatically convert a const Foo * to a plain Foo * in order to do so.
I solved the problem by changing the type of the set as follows.
QSet<Foo const*> set;
With this change, compilation succeeds.
You're violating the const-ness of the pointer returned from your get() function. get() returns a pointer to a const object of type Foo, but then you try to insert it into a vector of non-const Foo pointers. You need to const_cast the return value from get() or change the return type of get() from const Foo* to just Foo*.

equal_range and 2 overloads have no legal conversion for 'this' pointer

Here is this simple code
#include <map>
class MyMap : public std::multimap<int*, int*>
{
public:
void foo(const int* bar) const
{
equal_range(bar);
}
};
int main()
{
MyMap myMap;
int number;
myMap.foo(&number);
return 0;
}
It doesn't compile, and give the following error
error C2663: 'std::_Tree<_Traits>::equal_range' : 2 overloads have no legal conversion for 'this' pointer
I have seen many topic about this error, and it seems that it is a const issue. It compiles fine if I turn foo(const int* bar) into foo(int* bar).
Problem is, I don't see how foo content is supposed to change anything to my MyMap object. std::multimap proposes a const version of equal_range:
http://www.cplusplus.com/reference/map/multimap/equal_range/
What is my problem?
Thank you
Check the definition of equal_range:
pair<const_iterator,const_iterator> equal_range (const key_type& k) const;
It expects a constant reference to key_type: const key_type& k.
What you were trying to supply was a pointer to a constant integer: const int* bar
Why doesn't this work even though both values are const?
A constant reference to an integer const int& foo means that you cannot let foo refer to another integer, but it is allowed to change the value of the referenced integer.
A pointer to a constant integer const int* foo means that you can let foo point to another integer, but you cannot change the value of the integer it points to.
What the map actually expects is a const int*& k, but the map will automatically convert this if you supply a int* only (without the const).
[Edit]
Also note that a MyMap object still cannot be changed by your foo function even if you change const int* to int* as there still is another const at the end of your foo function. This const at the very end declares the function as constant meaning that it cannot modify the current object in which it is executed. If it was trying to modify it or call anything that could potentially modify it, you would get a compiler error. (Disclaimer: There are ways to modify a class from within a const function anyway but that's another topic.)
If fact the right compiler message can give you the answer. For one of the two overloads (the const one):
/usr/local/include/c++/v1/map:1836:41: note: candidate function not viable: 1st argument ('const int *')
would lose const qualifier
const int* is a pointer to a const int. The method expects a const key_type&, i.e. const int*& argument, that is a const reference to a (non-const) int*.
When confused, prefer to write const int* as int const*, which is the same thing. Then you'll see the difference to const int*& more clearly.
For your code to work, use int* instead of const int*.
I believe that the problem has to do with the mismatch on the key_type.
On one hand you have a multimap where key_type=int* while on the other hand you are passing a key_type=const int* thus attempting to drop the const qualifier on the key_type. I was confused by this too because I was expanding the key_type in my mind to get const int*& which should be compatible. However, the mismatch happens earlier on the key_type itself. At least that's the only logical explanation I could think of.
My suggestion would be to make the key_type const int* and keep your function parameter as is. After all, why would you need a pointer to a mutable value as a key to a map ?

const to Non-const Conversion in C++

I'm really annoyed by const keyword these days, as I'm not quite familiar with it. I had a vector that stores all const pointers like vector<const BoxT<T> *> *Q_exclude, and in the constructor of another class, I need an element in this queue to be passed in as a parameter and assign it to a non-const member. My question is:
How do I assign a const variable to a non-const variable? I know this doesn't make sense because after all, a const is a const, and should not be changed by any mean. But that annoying member variable REALLY has to be changed during the process! I might also change the data type in the vector to be non-const, but that would be too much work. Or does anyone know how to avoid such situation?
You can assign a const object to a non-const object just fine. Because you're copying and thus creating a new object, constness is not violated.
Like so:
int main() {
const int a = 3;
int b = a;
}
It's different if you want to obtain a pointer or reference to the original, const object:
int main() {
const int a = 3;
int& b = a; // or int* b = &a;
}
// error: invalid initialization of reference of type 'int&' from
// expression of type 'const int'
You can use const_cast to hack around the type safety if you really must, but recall that you're doing exactly that: getting rid of the type safety. It's still undefined to modify a through b in the below example:
int main() {
const int a = 3;
int& b = const_cast<int&>(a);
b = 3;
}
Although it compiles without errors, anything can happen including opening a black hole or transferring all your hard-earned savings into my bank account.
If you have arrived at what you think is a requirement to do this, I'd urgently revisit your design because something is very wrong with it.
Changing a constant type will lead to an Undefined Behavior.
However, if you have an originally non-const object which is pointed to by a pointer-to-const or referenced by a reference-to-const then you can use const_cast to get rid of that const-ness.
Casting away constness is considered evil and should not be avoided. You should consider changing the type of the pointers you use in vector to non-const if you want to modify the data through it.
The actual code to cast away the const-ness of your pointer would be:
BoxT<T> * nonConstObj = const_cast<BoxT<T> *>(constObj);
But note that this really is cheating. A better solution would either be to figure out why you want to modify a const object, and redesign your code so you don't have to.... or remove the const declaration from your vector, if it turns out you don't really want those items to be read-only after all.
Leaving this here for myself,
If I get this error, I probably used const char* when I should be using char* const.
This makes the pointer constant, and not the contents of the string.
const char* const makes it so the value and the pointer is constant also.
void SomeClass::changeASettingAndCallAFunction() const {
someSetting = 0; //Can't do this
someFunctionThatUsesTheSetting();
}
Another solution is to call said function in-between making edits to variables that the const function uses. This idea was what solved my problem being as I was not inclined to change the signature of the function and had to use the "changeASettingAndCallAFunction" method as a mediator:
When you call the function you can first make edits to the setting before the call, or (if you aren't inclined to mess with the invoking place) perhaps call the function where you need the change to the variable to be propagated (like in my case).
void SomeClass::someFunctionThatUsesTheSetting() const {
//We really don't want to touch this functions implementation
ClassUsesSetting* classUsesSetting = ClassUsesSetting::PropagateAcrossClass(someSetting);
/*
Do important stuff
*/
}
void SomeClass::changeASettingAndCallAFunction() const {
someFunctionThatUsesTheSetting();
/*
Have to do this
*/
}
void SomeClass::nonConstInvoker(){
someSetting = 0;
changeASettingAndCallAFunction();
}
Now, when some reference to "someFunctionThatUsesTheSetting" is invoked, it will invoke with the change to someSetting.