understanding std::find_if() using a lamda on std::pair - c++

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.

Related

Vector Find Error: no type named ‘iterator_category’ in ‘struct std::iterator_traits

myVector is a vector nodes. Each Node has an int value and a name that is a string pointer. I'm trying to make a simple function that will determine if any of the Nodes in this vector has this name.
vector<Node*>::iterator it;
it = std::find(*(*myVector.begin())->name, *(*myVector.end())->name, found);
if (it != myvector.end()) {
return true;
}
return false;
When I run this, I get the error "no type named ‘iterator_category’ in ‘struct std::iterator_traits". I'm guessing this has something to do with the setup of pointers, but I'm not sure what that would be or what this error means. Could someone explain it to me?
You are using the the algorithm and begin()/end() member functions incorrectly. First of all,
*myVector.end()
is already undefined behavior, as all container end() functions return a one-past-the-end, i.e. not dereferencable iterator. Dereferencing it leads to UB.
Second, the std::find call should be
const auto it = std::find_if(myVector.cbegin(), myVector.cend(),
[&found](const Node *n){ return n->name() == found; });
You need to specify a custom predicate - here, a lambda expression - because a find algorithm can only iterate over the nodes, not their names, and hence you must specify the way it determines a match by calling Node::name() and comparing it to found.
Let me add a couple of additional notes:
To be able to pass the custom predicate, you need the std::find_if instead of std::find algorithm.
I have used the cbegin() and cend() functions, as the container will not be modified.
Using type deduction (the auto) for iterators is widely accepted and simplifies their usage.
You might want to improve your naming. it could be firstMatchingNode, and found e.g. lookupName.
If you are only interested in whether there is at least one matching node name, consider std::any_of with the same predicate. This is closer to your intent, as it discards the position of the match.

Pointer to nth element in a vector of list

I need to return a pointer to some value in my list
std::vector<std::list<int>> lists;
...
if(lists.at(i).front()==val){
//return a pointer after the first value
return ptr;
}
The best way i can think of is using std::next, but i have no idea how to make it returns a pointer
The best way i can think of is using std::next, but i have no idea how to make it returns a pointer
std::next returns an iterator. You can use the indirection operator to get the pointed object. You can use the addressof operator to get the memory address (i.e. a pointer) of that object. So:
auto it = whatever; // iterator to the object whose pointer you want
return &*it; // return the pointer
Not relevant to int, but for some rare cases, you may need to use std::addressof in case the the type has an overloaded addressof operator.
Also, I recommend considering whether it makes any sense to return a pointer rather than the iterator itself.
I think of a function like this:
std::list<int>::iterator get(std::vector<std::list<int>>& lists, int i,int val)
{
assert(i<lists.size());
if(lists.at(i).front()==val)
{
//return a pointer after the first value
std::list<int>::iterator it = lists.at(i).begin();
return it++;
}
return lists.at(i).end();
}
Iterators are just "better" pointers. That is, you can use them as pointers (access value by dereferencing them)
auto it = lists.at(i).begin() // returns iterator to first element in i-th list in vector
...
*it // you can access value as if "it" was pointer
it++; // iterators can be "moved", that is, they will point to next element than before
if you really wish to return pointer type *int, instead of std::list<int>::iterator, you dereference it and gain reference back again
return &(*it);
for more on iterators, you can look here. You can see that there are many types of iterators, but all can be dereference as if they were pointers.
std::list<>:iterator is bidirectional iterator (they don't have random access, but you can move them by one element at a time, forward or backward)

Weird behavior with map.find and a pointer to a vector

I have a map of pairs to a vector of vectors that looks like this:
std::map<std::pair<uint16, uint16>, std::vector<std::vector<uint32> > >
The map is populated in the constructor of a class. This class provides a public method that returns a pointer to std::vector<std::vector<uint32> > (the map value part), with something like this:
typedef std::pair<uint16, uint16> key;
typedef std::vector<std::vector<uint32> > value;
value* FindValues(key someKey) {
std::map<key, value>::const_iterator it;
it = someStore.find(someKey);
if (it != someStore.end())
return &(value)it->second;
return NULL;
}
This is when it gets weird. When iterating over the vector returned by FindValues, all child vectors have a large, negative number (such as -1818161232) as their first value. But if I use a function like:
value FindValues(key someKey) {
std::map<key, value>::const_iterator it;
return someStore.find(someKey)->second;
}
...then the value is normal. This only happens for the value at index 0 of all child vectors. With the second method, though, my application segfaults if a key wasn't found (for obvious reasons). What am I doing wrong?
If the return statement truly looks as
return &(value) it->second;
then there are several things that can be said about it:
Your compiler is broken if it accepts it without issuing diagnostic messages. In C++ it is illegal to apply built-in unary & to a result of non-reference cast. The (value) it->second expression produces a temporary object, an rvalue. You can't obtain the address of such object by using &. The code should not even compile.
If your compiler accepts it as some kind of weird "extension", then it means that you are indeed obtaining and returning the address of a temporary object. The temporary object is then immediately destroyed, leaving your pointer pointing to garbage. No wonder you see some weird values through such pointer.
The need for some sort of cast arises from the fact that you used const_iterator to store the result of the search. Apparently you made a misguided attempt to cast away constness of it->second with your (value) cast. The correct way to do it might look as follows
return const_cast<value *>(&it->second);
But why did you use const_iterator in the first place? The right thing to do would be to use a regular iterator and just do
return &it->second;
without any extra casts.
You need to decide what kind of FindValue method you are trying to write. If this is supposed to be a constant method, it should return const value * and should be declared as const
const value* FindValues(key someKey) const
and, of course, you should use const_iterator inside in this case.
If your FindValue is supposed to be a non-constant method, then you can keep the current declaration
value* FindValues(key someKey)
but use ordinary iterator inside.
What you have now is some sort of hybrid of the two, which is what makes you to resort to weird casts. (In fact, you will probably need both versions in your class. One can be implemented through the other.)
Your typedefs are quite misleading. This is the erroneous line:
return &(value)it->second;
What appears to be a simple C-style type cast is actually a call to std::vector's copy constructor. This line could be rewritten as
return &std::vector<std::vector<uint32> >(it->second)
The reason for the weird results become visible when you rewrite this line as following:
std::vector<std::vector<uint32> > result (it->second);
return &result;
You are actually returning the address of a local object that will be destroyed as soon as the function returns.
So, this variant will be better.
typedef std::pair<uint16, uint16> key;
typedef std::vector<std::vector<uint32> > value;
value* FindValues(key someKey) {
std::map<key, value>::const_iterator it;
it = someStore.find(someKey);
if (it != someStore.end())
return &const_cast<value&>(it->second);
return 0;
}

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;
}

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

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.