C++ const std::map reference fails to compile - c++

Is there a reason why passing a reference to a std::map as const causes the [] operator to break? I get this compiler error (gcc 4.2) when I use const:
error: no match for ‘operator[]’ in
‘map[name]’
Here's the function prototype:
void func(const char ch, std::string &str, const std::map<std::string, std::string> &map);
And, I should mention that there is no problem when I remove the const keyword in front of std::map.
If I've been instructed correctly, the [] operator will actually insert a new pair into the map if it doesn't find the key, which would of course explain why this happens, but I can't imagine that this would ever be acceptable behavior.
If there is a better method, like using find instead of [], I'd appreciate it. I can't seem to get find to work either though... I receive const mismatched iterator errors.

Yes you can't use operator[]. Use find, but note it returns const_iterator instead of iterator:
std::map<std::string, std::string>::const_iterator it;
it = map.find(name);
if(it != map.end()) {
std::string const& data = it->second;
// ...
}
It's like with pointers. You can't assign int const* to int*. Likewise, you can't assign const_iterator to iterator.

When you're using operator[], std::map looks for item with given key. If it does not find any, it creates it. Hence the problem with const.
Use find method and you'll be fine.
Can you please post code on how you're trying to use find() ?
The right way would be :
if( map.find(name) != map.end() )
{
//...
}

If you're using C++11, std::map::at should work for you.
The reason std::map::operator[] doesn't work is that in the event of the key you're looking for not existing in the map, it will insert a new element using the provided key and return a reference to it (See the link for details). This is not possible on a const std::map.
The 'at' method, however, will throw an exception if the key doesn't exist. That being said, it is probably a good idea to check for the existence of the key using the std::map::find method before attempting to access the element using the 'at' method.

Probably because there is no const operator[] in std::map. operator[] will add the element you're looking for if it doesn't find it. Therefore, use the find() method if you want to search without the possibility of adding.

For your "const mismatched iterator errors" :
find() has two overloads:
iterator find ( const key_type& x );
const_iterator find ( const key_type& x ) const;
My guess is that you're getting this error because you're doing something like assigning a non-const iterator (on the left) to the result of a find() call on a const map:
iterator<...> myIter /* non-const */ = myConstMap.find(...)
That would result in an error, though perhaps not the one you're seeing.

Related

understanding std::find_if() using a lamda on std::pair

It is my understanding that std::find_if() below returns an iterator to the first element in the range of arg for which the third argument (lamda function) returns true. Is that correct?
Could somebody explain why there isn't an iterator defined like this std::pair<std::string, std::type_index>::iterator = std::find_if(...)?
Where is the iterator returned by std::find_if() stored and how can you call ->second on its own and not on an iterator?
std::type_index argType(const std::string& name) const
{
return std::find_if(args_.begin(), args_.end(),
[&name](std::pair<std::string, std::type_index> arg)->bool
{
return arg.first == name;
}
)->second;
}
It is my understanding that std::find_if() below returns an iterator
Yes, but that iterator is then dereferenced within the same expression. The function returns a copy of the std::type_index from some element of args_, which is presumably some std-like container with a value_type of std::pair<std::string, std::type_index> (or similar).
At a guess it's a std::vector<std::pair<std::string, std::type_index>>, because if it were a map the whole function could be simplified to
return args_.at(name);
Could somebody explain why there isn't an iterator defined like this std::pair<std::string, std::type_index>::iterator = std::find_if(...)?
Well firstly std::pair doesn't have a member iterator. I assume you meant std::vector<std::pair<std::string, std::type_index>>::iterator (or whichever collection type args_ is).
There doesn't need to be a separate statement declaring such an iterator, in much the same way as you don't need a separate double in the statement
return floor(3.14 * radius);
when you want an integer circumference calculation.
Where is the iterator returned by std::find_if() stored
Anywhere the compiler likes. It only exists while the return statement is being evaluated.
how can you call ->second on its own
You aren't. You are calling it on the temporary returned by std::find_if
and why is there a != args_.end() after the closing brace of std::find_if?
There isn't. The author assumes that they will find a matching element. If they don't, the program has undefined behaviour. That may be intentional, or it may be a bug.

How to create an unordered_map with non-stl types such as UnicodeString from ICU?

I'd like to be able to do this:
std::unordered_map<icu::UnicodeString, icu::UnicodeString> mymap;
However, when I do (and I come to use it) I was getting "cannot convert size_t to UnicodeString" errors. So I had a look around and read up on unordered containers. This blog post makes the point that I need to make available a specialisation of std::hash<icu::UnicodeString>, so I did exactly that:
namespace std
{
template<>
class hash<icu::UnicodeString> {
public:
size_t operator()(const icu::UnicodeString &s) const
{
return (size_t) s.hashCode();
}
};
};
Not perfect, however, it satisfies the requirements. However, now I'm getting errors that stem from:
error C2039: 'difference_type' : is not a member of 'icu_48::UnicodeString'
The blog post itself hints that I need to be doing more; however, it doesn't tell me what I should do, ending on these remarks:
In addition to requiring a hash function, the unordered containers also need to be able to test two keys for equality. The canonical way for them to do this is with a version of operator==() defined at the global namespace. This is typically a function you are used to having to construct when creating new classes, but if you overlook it, you will be up against the same raft of incomprehensible compiler errors seen earlier in this article.
I didn’t have to deal with it in this article because the standard library already defines this operator for std::pair. Of course, when using std::pair you also have to make sure you have an equality operator for T1 and T2.
So, now I'm a little confused, because operator== is defined for UnicodeString.
So, using C++11, MSVC and GCC. Also compiling with Qt dependencies. Then, my question is, what more do I need to do in order to add icu::UnicodeString types to an unordered map?
As requested, I'm later attempting to iterate over the map. The map itself is part of a class, called this->mymap:
std::unordered_map<icu::UnicodeString, icu::UnicodeString>::const_iterator it;
for ( it = this->mymap.begin(); it != this->mymap.end(); ++it )
{
// access it->first, it->second etc...
}
As OP discovered,
somebody had left a nice mymap->insert(key, value) which is wrong wrong wrong
Since an unordered map has a 2-argument insert method,
template <class P>
iterator insert(const_iterator hint, P&& obj);
the compiler will try to match the key as a const_iterator, which is probably why the difference_type type member is requested (it is a member of an iterator).
The correct way to insert an entry is to insert a pair,
mymap.insert(std::make_pair(key, value));
or just use the "emplace" method,
mymap.emplace(key, value);

How can I check values in a C++ map without getting compiler errors in a "const" member function?

I have a member function that compares data in a C++ map. I declared the member function as constant:
bool operator == (UnitSet otherSet) const;
but as soon as I use myMap["l"] to access and compare values I get compiler errors about myMap being constant. Is there a way around this while keeping the const directive?
I know an empty value is created for the "l" key if it didn't exist when I check it, but that won't change the value / behaviour of my UnitSet - maybe I'm trying to be too picky here though and should just drop the "const". What is the good way to go about this?
Thanks.
Firstly, you should change your function signature to accept otherSet by const reference, as you have no need to copy it and won't be modifying it:
bool operator==(const UnitSet& otherSet) const;
Then, rather than using the non-const operator[], use find() or begin()/!=end()/++ to get const_iterators into your maps for the comparison. As find(), begin() and operations on const_iterator only require const access to map, they can be used from your const member function.
map<X, Y>::const_iterator i = the_map.find(key);
if (i != the_map.end())
// here, i->first is the key, i->second is the value
else
// key wasn't in the map after all...
std::map unfortunately doesn't have a const overload of the [] operator. This is because whenever you access a key that doesn't exist, the map creates an element for it; if the map was const, that would be impossible. Therefore, there's no way to call it with a constant map. It isn't related to the behavior of your UnitSet class.
You'll have to use myMap.find(TKey) to get an iterator to your element, which returns myMap.end() if it can't find it.
Map iterators point to a std::pair of your key and value, so you need to access the second member of it to get your value (first being the key).
const UnitSet& reference = myMap.find("l")->second;

Why can't I pass const map structure to a function in c++?

I tried to pass const with vector it works:
Ex:
void damn(const vector <bool> &bb)
{
for (int i=0; i<bb.size(); i++)
cout<<bb[i]<<endl;
}
But when trying with map, it does not:
void pas(const map <string, float> &mm)
{
cout<<mm["a"];
cout<<mm["b"];
}
I wonder why it doesn't.
map::operator[] is a little odd. It does this:
Look for the key.
If found, return it.
If not, insert it and default-construct its associated value.
Then return a reference to the new value.
Step 3 is incompatible with constness. Rather than have two differently-functioning operator[] overloads, the language forces you to use map::find for const objects.
Alternately, one could argue, what would map::operator[] const do if the argument is not in the map? Throw an exception? Undefined behavior? (After all, that's what vector::operator[] does with an index out of bounds.) In any case, the problem is avoided with only a small inconvenience to us.
my_map.find(key) returns my_map.end() if the key is not found.
std::map::operator[] inserts a default-constructed element if the requested element is not in the map. This is why it is not a const member function. You can use std::map::find instead, but be sure to check the iterator it returns.
I believe that it is because [] in map isn't const, as it creates new pair with default value, if you address to nonexisting one. Try
void pas(const map <string, float> &mm)
{
cout<<mm.find("a")->second;
cout<<mm.find("b")->second;
}

How do I escape the const_iterator trap when passing a const container reference as a parameter

I generally prefer constness, but recently came across a conundrum with const iterators that shakes my const attitude annoys me about them:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'const_iterator's which then prevent me from doing anything with modifing the result (which makes sense).
Is the solution, then, to give up on making the passed in container reference const, or am I missing another possibility?
This has always been my secret reservation about const: That even if you use it correctly, it can create issues that it shouldn't where there is no good/clean solution, though I recognize that this is more specifically an issue between const and the iterator concept.
Edit: I am very aware of why you cannot and should not return a non-const iterator for a const container. My issue is that while I want a compile-time check for my container which is passed in by reference, I still want to find a way to pass back the position of something, and use it to modify the non-const version of the list. As mentioned in one of the answers it's possible to extract this concept of position via "advance", but messy/inefficient.
If I understand what you're saying correctly, you're trying to use const to indicate to the caller that your function will not modify the collection, but you want the caller (who may have a non-const reference to the collection) to be able to modify the collection using the iterator you return. If so, I don't think there's a clean solution for that, unless the container provides a mechanism for turning a const interator into a non-const one (I'm unaware of a container that does this). Your best bet is probably to have your function take a non-const reference. You may also be able to have 2 overloads of your function, one const and one non-const, so that in the case of a caller who has only a const reference, they will still be able to use your function.
It's not a trap; it's a feature. (:-)
In general, you can't return a non-const "handle" to your data from a const method. For example, the following code is illegal.
class Foo
{
public:
int& getX() const {return x;}
private:
int x;
};
If it were legal, then you could do something like this....
int main()
{
const Foo f;
f.getX() = 3; // changed value of const object
}
The designers of STL followed this convention with const-iterators.
In your case, what the const would buy you is the ability to call it on const collections. In which case, you wouldn't want the iterator returned to be modifiable. But you do want to allow it to be modifiable if the collection is non-const. So, you may want two interfaces:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
MyList::iterator find( MyList & list, int identifier )
{
// do some stuff to find identifier
return retItor;
}
Or, you can do it all with one template function
template<typename T>
T find(T start, T end, int identifier);
Then it will return a non-const iterator if the input iterators are non-const, and a const_iterator if they are const.
What I've done with wrapping standard algorithms, is have a metaobject for determining the type of container:
namespace detail
{
template <class Range>
struct Iterator
{
typedef typename Range::iterator type;
};
template <class Range>
struct Iterator<const Range>
{
typedef typename Range::const_iterator type;
};
}
This allows to provide a single implementation, e.g of find:
template <class Range, class Type>
typename detail::Iterator<Range>::type find(Range& range, const Type& value)
{
return std::find(range.begin(), range.end(), value);
}
However, this doesn't allow calling this with temporaries (I suppose I can live with it).
In any case, to return a modifiable reference to the container, apparently you can't make any guarantees what your function does or doesn't do with the container. So this noble principle indeed breaks down: don't get dogmatic about it.
I suppose const correctness is more of a service for the caller of your functions, rather that some baby-sitting measure that is supposed to make sure you get your simple find function right.
Another question is: how would you feel if I defined a following predicate and then abused the standard find_if algorithm to increment all the values up to the first value >= 3:
bool inc_if_less_than_3(int& a)
{
return a++ < 3;
}
(GCC doesn't stop me, but I couldn't tell if there's some undefined behaviour involved pedantically speaking.)
1) The container belongs to the user. Since allowing modification through the predicate in no way harms the algorithm, it should be up to the caller to decide how they use it.
2) This is hideous!!! Better implement find_if like this, to avoid this nightmare (best thing to do, since, apparently, you can't choose whether the iterator is const or not):
template <class Iter, class Pred>
Iter my_find_if(Iter first, Iter last, Pred fun)
{
while (first != last
&& !fun( const_cast<const typename std::iterator_traits<Iter>::value_type &>(*first)))
++first;
return first;
}
Although I think your design is a little confusing (as others have pointed iterators allow changes in the container, so I don't see your function really as const), there's a way to get an iterator out of a const_iterator. The efficiency depends on the kind of iterators.
#include <list>
int main()
{
typedef std::list<int> IntList;
typedef IntList::iterator Iter;
typedef IntList::const_iterator ConstIter;
IntList theList;
ConstIter cit = function_returning_const_iter(theList);
//Make non-const iter point to the same as the const iter.
Iter it(theList.begin());
std::advance(it, std::distance<ConstIter>(it, cit));
return 0;
}
Rather than trying to guarantee that the list won't be changed using the const keyword, it is better in this case to guarantee it using a postcondition. In other words, tell the user via comments that the list won't be changed.
Even better would be using a template that could be instantiated for iterators or const_iterators:
template <typename II> // II models InputIterator
II find(II first, int identifier) {
// do stuff
return iterator;
}
Of course, if you're going to go to that much trouble, you might as well expose the iterators of MyList to the user and use std::find.
If you're changing the data directed by the iterator, you're changing the list.
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'cons_iterator's which then prevent me from doing anything with the result.
What is "dong anything"? Modifying the data? That's changing the list, which is contradictory to your original intentions. If a list is const, it (and "it" includes its data) is constant.
If your function were to return a non-const iterator, it would create a way of modifying the list, hence the list wouldn't be const.
You are thinking about your design in the wrong way. Don't use const arguments to indicate what the function does - use them to describe the argument. In this case, it doesn't matter that find() doesn't change the list. What matters is that find() is intended to be used with modifiable lists.
If you want your find() function to return a non-const iterator, then it enables the caller to modify the container. It would be wrong for it to accept a const container, because that would provide the caller with a hidden way of removing the const-ness of the container.
Consider:
// Your function
MyList::iterator find(const MyList& list, int identifier);
// Caller has a const list.
const MyList list = ...
// but your function lets them modify it.
*( find(list,3) ) = 5;
So, your function should take a non-const argument:
MyList::iterator find(MyList& list, int identifier);
Now, when the caller tries to use your function to modify her const list, she'll get a compilation error. That's a much better outcome.
If you're going to return a non-const accessor to the container, make the function non-const as well. You're admitting the possibility of the container being changed by a side effect.
This is a good reason the standard algorithms take iterators rather than containers, so they can avoid this problem.