How to sort gunichar in Glib::ustring? - c++

I am trying to sort characters in Glib::ustring by their Unicode position. The following code:
Glib::ustring str("hello");
std::sort(str.begin(), str.end());
produces the following error at compile time:
/usr/include/c++/10/bits/stl_algo.h:1975:22: error: no match for ‘operator-’ (operand types are ‘Glib::ustring_Iterator<__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> > >’ and ‘Glib::ustring_Iterator<__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> > >’)
1975 | std::__lg(__last - __first) * 2,
My understanding is that sort is trying to get the difference between the first and last characters (i.e. the length of the container), but it is not defined by Glib::ustring_Iterator.
I can get around the problem by copying into some other container, and the putting it back:
Glib::ustring str("hello");
std::vector<gunichar> vec(str.begin(), str.end());
std::sort(vec.begin(), vec.end());
str.assign(Glib::ustring{vec.begin(), vec.end()});
But this code feels like an ugly hack for a feature that i would expect to see in the library, but perhaps just not looking in the right place. Is this wheel already implemented?

Related

C++: No match for operator for the same types of operand

I have a vector of a map of int and float
std::vector<std::map<int, float>> datad;
And I want to iterate over the maps in vector and inside over pairs in map
for (std::vector<std::map<int, float>>::iterator currentMap = datad.begin(); currentMap < datad.end(); ++currentMap)
{
for (std::map<int, float>::iterator it = currentMap->begin(); it < currentMap->end(); ++it)
{/*some code here*/}
}
But in the second loop g++ compiler gives an error:
no match for ‘operator<’ (operand types are ‘std::_Rb_tree_iterator<std::pair<const int, float> >’ and ‘std::map<int, float>::iterator’ {aka ‘std::_Rb_tree_iterator<std::pair<const int, float> >’})
But isn't it looks like the types
std::_Rb_tree_iterator<std::pair<const int, float>>
std::_Rb_tree_iterator<std::pair<const int, float>>
are the same?
Yes, I see that compiler says the currentMap->end() have the type std::map<int, float>::iterator but...it is aka _Rb_tree_iterator...
maybe
static_cast<std::_Rb_tree_iterator<std::pair<const int, float>>>(currentMap->end())
What is the easiest way to continue using iterators and fix the problem?
PS. static_cast don't work, it gives
no match for ‘operator<’ (operand types are ‘std::map<int, float>::iterator’ {aka ‘std::_Rb_tree_iterator<std::pair<const int, float> >’} and ‘std::map<int, float>::iterator’ {aka ‘std::_Rb_tree_iterator<std::pair<const int, float> >’})
PSS. I've also tried auto but it also doesn't work (the same error as the first one)
std::map's iterators are bidirectional iterators, not random access iterators. Therefore they do not provide relational comparison operators. You can only equality-compare them.
So replace < with !=, which is the standard way of writing iterator loops. Or even better, replace the loop with a range-for loop:
for (auto& currentMap : datad)
{
for (auto& el : currentMap)
{
/* Use el here as reference to the (pair) element of the map. */
}
}

Error with map and string conversion in c++

I'm familiar with Python's dictionary function, and I'm assuming based on my research that C++'s map function is relatively similar. However I'm running into a "conversion" issue:
std::string dictionary(std::string inquiry){
std::map<std::string, std::string> mapK;
mapK["12th st. Oakland City Center"]="12th";
mapK["16th st. Mission"]="16th";
return mapK.find(inquiry);
}
This is a string function that should receive a string and then with the corresponding key return a string back to the main. Evidently there's an issue with the return statement.
error: could not convert 'mapK.std::map<_Key, _Tp, _Compare, _Alloc>::find<std::basic_string<char>, std::basic_string<char>, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, std::basic_string<char> > > >((*(const key_type*)(& inquiry)))' from 'std::map<std::basic_string<char>, std::basic_string<char> >::iterator {aka std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, std::basic_string<char> > >}' to 'std::string {aka std::basic_string<char>}'
return mapK.find(inquiry);
std::map::find() returns iterator, specifically std::map::iterator.
The only thing you need to fix is to put a dereference:
return *mapK.find(inquiry);
Or even better, just use operator[]:
return mapK[inquiry];
Do note though that if inquiry is not in the map, it will return empty string. In case of find, it is straight undefined behavior.
Your problem is with the return type of find. It does not return the mapped type. Rather it returns an iterator.
The signature for find is iterator find( const Key& key ); (see here; there is also the const overload).
You should also check whether the key was found. So you could write something like
MapType::iterator i = mapK.find(inquiry);
if (i != mapK.end())
return i->second;
else
return ""; // or handle error some other way
Alternatively you could use the member access operator[], but this will insert a default constructed element in the map if the key is not found.

+= operator on a pair and make_pair with templates

I came across the following while reading codebase of a library.
errorMap, used in the below code is defined as:
map <const string, pair<int, double>> errorMap;
And the relevant part of the code being:
errorMap["substitutions"] += make_pair<int,double>(targetLength, substitutions);
errorMap["insertions"] += make_pair<int,double>(targetLength, insertions);
errorMap["deletions"] += make_pair<int,double>(targetLength, deletions);
The above part is throwing this compilation error. When running the library through it's own build system, the code seems to be compiling. Can someone shed some light on what exactly is happening here?
PS: I already looked at the pair documentation at cppreference and other sites, none of them specify a += operator for a pair. This is the first time I'm encountering a make_pair with templated arguments, on which I can't find more information either.
It's not about the pair, it's about the map. The operator [] is used to insert or update elements in a map.
For example in a std::map<char, int> myMap{{'a', 27}, {'b', 3}, {'c', 1}};, I could do the following (as demonstrated in the page linked above):
myMap['a'] = 6; //to change the value associated to 'a'
myMap['d'] = 8; //to insert a new value
I could also do the following:
myMap['b'] += 9; //Now the value associated to b is 3 + 9 = 12
In the 3 lines of code posted in the question, the values associated with the strings inside of the brackets are being updated.
The operator+= have probably been overloaded for pairs with template. (Look at the answers to this question) This might be why, you're getting those errors instead of the following (replace char with string):
error: no match for ‘operator+=’ (operand types are ‘std::map<char, std::pair<int, double> >::mapped_type {aka std::pair<int, double>}’ and ‘std::pair<int, double>’)
Since the same operation doesn't reproduce the same errors, the poblem is from deeper implementations, for which you provided no context. They might be related to right and left values:
cannot convert ‘targetLength’ (type ‘int’) to type ‘int&&’
You [might][1] want to look at those answers for that.
[1]: Might because I'm not sure of what I'm saying. I wanted to contribute with a comment, but not enough rep, so I tried my best with an answer.

no match for ‘operator+=’ aka std::_Rb_tree_const_iterator std::map [duplicate]

This question already has answers here:
Error: "no match for operator+" , for list iterator
(4 answers)
Closed 6 years ago.
I have a map called assets returned from a const function, I use a const_iterator to get a subset of the map like this:
std::map<int, Asset>::const_iterator start = assets.begin();
start += 5;
......
But I got error: error: no match for ‘operator+=’ (operand types are ‘std::map<int, Asset>::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const int, Asset> >}’ and ‘int’)
This is because std::map iterators are BidirectionalIterators, not RandomAccessIterators - and hence support operator++ and operator-- but not operator+= and operator-=.
Use std::advance(start, 5) instead (bearing in mind that this will result in repeated invocation of operator++).

Removing an element with `vec.erase(find(...) )` if `find` should start searching in reverese order

I have a vector of unique(!) elements and want to remove one, with a certain value. It's also very likely that this element will be near the end of the vector. Hence I want to start looking for this element from the end.
I thought this should work, however it does not.
vec.erase( find(crbegin(vec), crend(vec), value) ); //does not work
The compiler sais (shorten):
error: no matching function for call to 'std::vector<unsigned int>::erase(std::reverse_iterator<__gnu_cxx::__normal_iterator<unsigned int*, std::vector<unsigned int> > >)'
and also
note: no known conversion for argument 1 from 'std::reverse_iterator<__gnu_cxx::__normal_iterator<unsigned int*, std::vector<unsigned int> > >' to 'std::vector<unsigned int>::const_iterator {aka __gnu_cxx::__normal_iterator<const unsigned int*, std::vector<unsigned int> >}'
If I don't use reverse iterators, it works (hence the compiler note):
vec.erase( find(cbegin(vec), cend(vec), value) ); //works
How can I tell find that it should start searching for value from the end?
edit: I know, that the vector contains the searched element.
You can't tell the vector to erase an element with some kind of iterator it doesn't know about. You need to turn the reverse iterator back into a std::vector::iterator. Do this with std::reverse_iterator::base(). However, this would give you an off-by-one error, since reverse iterators have to do some shifting to account for not having an "end" iterator at the beginning of the range. Use std::prev to account for this:
vec.erase(std::prev(find(crbegin(vec), crend(vec), value).base()));
Bear in mind that this assumes that the element is found, however. Your code already does this and your edit says you assume this deliberately, so I'm not introducing a new problem, but in reality you should check the element is found before trying to erase it. Something like (untested):
auto foundCRIt = std::find(std::crbegin(vec), std::crend(vec), value);
if (foundCRIt == std::crend(vec)) {
std::cerr << "Not found!\n";
return;
}
vec.erase(std::prev(foundCRIt.base()));