Insert new pair in std::map<CString, CString> - c++

I have two lists and a name to find. If the name to find is not in the first list then it might be in the second list with a slightly different format. Conversion function between the two formats are given.
std::map<CString, CString>* convertedNames;
BOOL CSome::SeekNameWithConversion(std::set<CString> names, CString nameToFind)
{
for (auto it = names.begin(); it != names.end(); ++it)
{
if (nameToFind.Compare(*it) == 0) return true;
auto convertedIt = convertedNames->find(*it);
if (convertedIt != convertedNames->end() &&
nameToFind.Compare(convertedIt->second) == 0)
return true;
CString justConvertedName = ConvertToTheOtherFormat(nameToFind);
convertedNames->insert(*it, justConvertedName); // Error here
return nameToFind.Compare(justConvertedName) == 0;
}
}
The error which appears is:
error C2675: unary '++':
'ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<_CharType>>>' does
not define this operator or a conversion to a type acceptable to the
predefined operator
I would like to know why the operator ++ is involved here and then how should I treat this error.

Most of the various insert functions of std::map require an iterator. Instead, you pass the pointed-to-object, (which is a CString, I suppose):
convertedNames->insert(*it, justConvertedName);
^^^
this is a CString, not a std::map<CString,CString>::iterator
If you want to insert a key-value pair, use the map's value_type instead which is basically a std::pair made up of key and value:
convertedNames->insert(std::make_pair(*it, justConvertedName));

The first argument to map::insert is an iterator, not a CString. Internally, the method is trying to increment the iterator. This apparently makes a call to operator++. You don't need to use this insert overload. It's intended to improve performance when you know a position close to where the element will be inserted. Just call convertedNames->insert(std::make_pair(*it, justConvertedName)) instead.

So the first thing to understand is that templates are always fully implemented in the header, as any classes required are built with that object (just think if the std lib had all possible std::vector implementations built in!)
This means that the implementation of the template is exposed - and in this case, there's a ++ somewhere there. If you take the whole error printed (it'll be quite a few more lines) you may even be told which parameter you've got wrong.
In any case, we can see that *it is clearly going to be a CString, but I'd guess that this
... convert to the other format
is probably not returning what you think

Related

No Matching function to call. C++ STL

I'm new to C++ STL. I'm writing a function to check whether one vector is an subset of another(Duplicate elements also count) and print 'Yes' or 'No'. I have come up with the following code :
void checkMagazine(vector<string> magazine, vector<string> note) {
vector<string>::iterator a;
for(int i=0;i<note.size();i++)
{
a=find(magazine.begin(),magazine.end(),note[i]);
if(a==magazine.end())
{
printf("No");
return;
}else magazine.erase(a-magazine.begin());
}
printf("Yes");
}
But,I'm getting the following compilation error :
error: no matching function for call to ‘std::vector<std::__cxx11::basic_string<char>
>::erase(__gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char>*,
std::vector<std::__cxx11::basic_string<char> > >::difference_type)’
}else magazine.erase(a-magazine.begin());
I'm trying to use erase to delete the found element in magazine.Is there some problem with the type of declaration of the iterator? Why is this happening?
And also if there are alternate methods/logic/inbuilt-functions to get the required job done using STL, Please let me know since I'm new to this and trying to learn it.
erase takes an iterator. You have that iterator, it's called a. But you then convert the iterator a into an offset relative to begin(). erase does not have an overload which takes an offset.
The Standard Library has alternatives if the vectors would be sorted, but we don't see the calling code so we can't assume that.

C++ set iterator error: no match for ‘operator+=’

I am trying to iterate over a set of objects and create a comma-separated string of their names. The commented-out part of the code is supposed to add a comma only if it is not the last one in the set, but it generates the following error:
error: no match for ‘operator+=’ (operand types are ‘std::_Rb_tree_const_iterator’ and ‘long int’)
_M_current += -__n;
It works fine if I use a vector instead of a set. What am I doing wrong?
std::string paramList = "";
std::set<Param>::iterator end = params.end();
for (std::set<Param>::iterator it = params.begin(); it != end; ++it) {
paramList += (*it).name;
/*if (it != end -1) {
paramList += ",";
}*/
}
Only certain categories of iterator allow you to directly perform arbitrary arithmetic, like end-1.
The iterator of a set permits going forwards and backwards one step at a time, using it++ and it--. It is true that it - 1 still only traverses one step, but the rule is more general (the language doesn't know that the integer you're providing is just 1 and has no reason to make a special case compared to, say, 42).
It is possible to use std::next/std::prev to get this behaviour. This is deliberately more verbose, in order to discourage you from arbitrary arithmetic on the iterator, which is more costly for this kind of iterator than it would be for a simple array-like thing such as a vector.
In this case, std::prev(end) will work for you, and be reasonably idiomatic.
But, if you find yourself writing something like std::next(it, 42), you probably want to consider improving your algorithm, or using a different container.
Again, the purpose of this restriction is to encourage exactly that thinking.
Another way to write your algorithm might be:
std::string paramList;
std::set<Param>::iterator begin = params.begin(), end = params.end();
for (std::set<Param>::iterator it = begin; it != end; ++it) {
if (it != begin)
paramList += ",";
paramList += (*it).name;
}
Notice how I've flipped the logic to avoid the need to do any arithmetic at all.
I actually usually use a bool flag for this (when my paramList is a stringstream) or just always append a , then shrink paramList by one later (when it's a string) if params was non-empty.
end -1
This part is causing the problem. std::set only has Bidirectional iterators, so it only accepts operator ++ and operator -- on it.
You should use std::prev form <iterator> header instead:
if (it != std::prev(end))

c++ stl iterators

I am getting a lot of errors trying to use an iterator for three dimensional vector of sets of ints. See the following code (which is just select pieces, because the whole thing is too long; I think this should be enough to see what is wrong, but let me know if it isn't):
vector<vector<vector<set<int> > > > particles_celllist;
vector<vector<vector<set<int> > > >::iterator cell_iter;
map<int,map<int,Particle> > particle_grid;
for (cell_iter=particles_celllist[wx][wy][wz].begin();cell_iter!=particles_celllist[wx][wy][wz].end();cell_iter++)
{
double distance_to_cell=sqrt(pow(particles[*cell_iter].position().y()-(wy)*ygridlength,2)+
pow(particles[*cell_iter].position().z()-(wz)*zgridlength,2));
if (distance_to_cell<input_data.diam_large())
{
particle_grid[box_counter][*cell_iter]=particles[*cell_iter];
}
}
Note: wx, wy, wz, and box_counter are ints, ygridlength and zgridlength are doubles, and Particle::position::y (or ::z) and input_data::diam_large return doubles.
I get a multitude of errors:
no match for operator "=" in
"cell_iter=particles_celllist[wx][wy][wz].begin()"
no match for operator "!=" in
"cell_iter!=particles_celllist[wx][wy][wz].end()"
no match for operator "[]" whenever I used [*cell_iter] to call
something
I get the feeling like the error somehow stems from the iterator itself, but I haven't been able to figure it out.
You want set<int>::iterator cell_iter;. Just look again carefully at whose begin() function you're calling.
In C++11 you would of course just have said
auto cell_iter = particles_celllist[wx][wy][wz].begin()
and never noticed that this is hard :-)
particles_celllist[wx][wy][wz] is of type set<int>, so particles_celllist[wx][wy][wz].begin() is of type set<int>::iterator

Why can't I remove a string from a std::set with std::remove_if? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
remove_if equivalent for std::map
I have a set of strings:
set <wstring> strings;
// ...
I wish to remove strings according to a predicate, e.g.:
std::remove_if ( strings.begin(), strings.end(), []( const wstring &s ) -> bool { return s == L"matching"; });
When I attempt this, I get the following compiler error:
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\algorithm(1840): error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::basic_string<_Elem,_Traits,_Ax>'
The error appears to suggest that std::string doesn't have a by-value copy constructor ( which would be illegal). Is it somehow bad to use std::remove_if with std::set ? Should I be doing something else instead such as several iterations of set::find() followed by set::erase() ?
std::remove_if (or std::erase) works by reassigning the values of the members of the range. It doesn't understand how std::set organizes data, or how to remove a node from its internal tree data structure. Indeed, it's impossible to do so using only references to nodes, without having the set object itself.
The standard algorithms are designed to have transparent (or at least consistently easy-to-remember) computational complexities. A function to selectively remove elements from a set would be O(N log N), due to the need to rebalance the tree, which is no better than a loop calling my_set.remove() . So, the standard doesn't provide it, and that is what you need to write.
On the other hand, a naively hand-coded loop to remove items from a vector one-by-one would be O(N^2), whereas std::remove_if is O(N). So the library does provide a tangible benefit in that case.
A typical loop (C++03 style):
for ( set_t::iterator i = my_set.begin(); i != my_set.end(); ) {
if ( condition ) {
my_set.erase( i ++ ); // strict C++03
// i = my_set.erase( i ); // more modern, typically accepted as C++03
} else {
++ i; // do not include ++ i inside for ( )
}
}
Edit (4 years later!): i ++ looks suspicious there. What if erase invalidates i before the post-increment operator can update it? This is fine, though, because it's an overloaded operator++ rather than the built-in operator. The function safely updates i in-place and then returns a copy of its original value.
The error message says
no operator found which takes a left-hand operand of type 'const
std::basic_string<_Elem,_Traits,_Ax>'
Note the const. The compiler is correct that std::wstring doesn't have an operator= which can be called on a const object.
Why is the string const? The answer is that the values in a std::set are immutable, because values in a set are ordered, and changing a value could change its ordering in the set, invalidating the set.
Why is the compiler trying to copy a value of the set?
std::remove_if (and std::remove) don't actually erase anything (nor can they, because they don't have the container, only iterators). What they do is to copy all values in the range which don't match the criterion to the beginning of the range, and return an iterator to the next element after the matching elements. You are then supposed to manually erase from the returned iterator to the end of the range. Since a set keeps its elements in order, it would be wrong to move any elements around, so remove_if cannot be used on a set (or any other associative container).
In short, you do have to use a loop of std::find_if and set::erase, like so:
template<class V, class P>
void erase_if(std::set<V>& s, P p)
{
std::set<V>::iterator e = s.begin();
for (;;)
{
e = std::find_if(e, s.end(), p);
if (e == s.end())
break;
e = s.erase(e);
}
}

Error C2678 after migrating C++ code from VC6 to VS2008 - no operator found which takes a left-hand operand of type 'type'

This piece of code compiles file in VC6 but in VS 2008 it gives an error. Can anyone tell me why?
I guess it is because you can no longer compare a pointer to NULL (which is a typedef for 0).
If that is the case, how do I do this comparison in VC9?
for ( std::vector<aCattrBase*>::iterator iT = attrLst.begin(); iT < attrLst.end(); iT++)
{
if ( (iT != NULL) && (*iT != NULL) ) //Error: C2678
{
//code
}
}
error C2678: binary '!=' : no operator
found which takes a left-hand operand
of type
'std::_Vector_iterator<_Ty,_Alloc>'
(or there is no acceptable conversion)
The type for 'std::vector::iterator' is not necessarily a pointer type so you can not compare it to NULL.
In your old compiler it just happened to be a pointer and so your code compiled. But you just got lucky (as shown when you moved the code to a different compiler).
The only test on iterator you have is to compare it to end() or begin() or any valid iterator within the range begin() -> end(). Since this is a vector you can do mathematical operations with the iterator. iT-begin() should give you an offset. But this is not valid for all containers (check each containers documentation).
All you need to do is test what the iterator points at:
for ( std::vector<aCattrBase*>::iterator iT = attrLst.begin();
iT != attrLst.end(); // Changed this. Notice the !=
++iT) // Changed this. Prefer pre increment for not integer types
{
if ( *iT != NULL)
{
//code
}
}
The iterator is not a pointer, it's a class instance and does not have the binary operator != to compare it with null.
You're trying to compare the iterator to NULL in the first condition in the if statement. You do not need this first comparison as the iterator iT should alays be within the valid portion of the list.
Comparing an iterator to NULL was never legal. VC6 let you do it, but was wrong to do so.
In the example you give, the comparison is meaningless, as the iterator will always point to something. Checking (*IT)!=NULL is reasonable and still works.
If there is a real prospect that an iterator does not point to a valid object, VC9 has an undocumented function
IT._Has_container()
that will be true if an iterator points to a container and false if the iterator does not. To set the iterator to nothing, you assign an empty iterator:
IT = std::vector<aCattrBase*>::iterator();
The above represents non-portable code and fairly poor style and I don't recommend designing anything to use it. However if you need to quickly get some VC6 code to compile on VC9, it might get you out of trouble.