How to find element in map where key is a shared_ptr? - c++

I have a map of std::shared_ptr's for both key and value and I'm trying to find the right element. Below is what I have, though, it still has a problem and won't compile.
std::map<std::shared_ptr<MyObjA>, std::shared_ptr<ValObj>> aobjs;
std::map<std::shared_ptr<MyObjB>, std::shared_ptr<ValObj>> bobjs;
template <typename T>
int findit(T& t)
{
T myobj = t;
typename std::map<std::shared_ptr<T>, std::shared_ptr<ValObj>>::iterator it;
if constexpr(std::is_same<T, net::MyObjB>::value) {
net::MyObjB objB;
// failing here ...
auto it = std::find_if(bobjs.begin(), bobjs.end(),
[&](std::shared_ptr<MyObjB> const& p) {
return *p == objB;
});
if (it != bobjs.end()) {
// found it
return 0;
}
}
return -1;
}
I think the problem is with the first and last of the find_if returning the std::shared_ptr, but unsure. I got a ton of output that I've had trouble parsing ...
/usr/lib/gcc/x86_64-pc-linux-gnu/10.3.0/include/g++-v10/bits/predefined_ops.h: In instantiation of ‘constexpr bool __gnu_cxx::__ops::_Iter_pred<_Predicate>::operator()(_Iterator) [with _Iterator = std::_Rb_tree_iterator<std::pair<const std::shared_ptr<MyObjB>, std::shared_ptr<ValObj> > >; _Predicate = findit<MyObjB>::<lambda(const std::shared_ptr<MyObjB>&)>]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/10.3.0/include/g++-v10/bits/stl_algobase.h:1912:42: required from ‘constexpr _InputIterator std::__find_if(_InputIterator, _InputIterator, _Predicate, std::input_iterator_tag) [with _InputIterator = std::_Rb_tree_iterator<std::pair<const std::shared_ptr<MyObjB>, std::shared_ptr<ValObj> > >; _Predicate = __gnu_cxx::__ops::_Iter_pred<findit<MyObjB>::<lambda(const std::shared_ptr<MyObj>&)> >]’
/usr/lib/gcc/x86_64-pc-linux-gnu/10.3.0/include/g++-v10/bits/stl_algobase.h:1974:23: required from ‘constexpr _Iterator std::__find_if(_Iterator, _Iterator, _Predicate) [with _Iterator = std::_Rb_tree_iterator<std::pair<const std::shared_ptr<MyObjB>, std::shared_ptr<ValObj> > >; _Predicate = __gnu_cxx::__ops::_Iter_pred<findit<MyObjB>::<lambda(const std::shared_ptr<ValObj>&)> >]’
/usr/lib/gcc/x86_64-pc-linux-gnu/10.3.0/include/g++-v10/bits/stl_algo.h:3934:28: required from ‘constexpr _IIter std::find_if(_IIter, _IIter, _Predicate) [with _IIter = std::_Rb_tree_iterator<std::pair<const std::shared_ptr<MyObjB>, std::shared_ptr<ValObj> > >; _Predicate = findit<MyObjB>::<lambda(const std::shared_ptr<MyObjB>&)>]’
Thoughts?

I have a map of std::shared_ptr's for both key and value
... thoughts?
A few things come to mind:
1.
It is likely not a good idea to hold a map like that - especially with respect to the keys. It doesn't make sense to have "heavy" keys, that require resource allocation, and which you are likely avoiding holding many copies of.
It is likely that MyObjA's have some kind of cheap numeric identifier you can use; and if they don't - consider adding such a field, and constructing MyObjA with such a unique identifier. Then, instead of a map from MyObjA's to MyObjB's, you could map from ObjAKey to MyObjB's, or even from ObjAKey to std::pair's of a MyObjA and MyObjB.
2.
I suspect that even for the map values, a std::shared_ptr may not be necessary, but you haven't given us enough context.
3.
Do you really need an ordered map? Would std::unordered_map not work for you? Also, if the number of elements in your map is not big enough, you can probably just use a vector of MyObjA, MyObjB pairs and it'll be good enough. It's not as though the standard library maps are fast.

Related

Why does const does not works with size() for stl map, whereas it works perfectly for other containers ? [duplicate]

This question already has answers here:
Why isn't the [] operator const for STL maps?
(6 answers)
Closed 7 years ago.
Encountered while working on a problem which is hard to describe/explain here as a whole so here is the relevant recreation of the problem.
compiling this code with gnu g++ on windows
int recreate(const map <int , vector<string> > &bitFieldMap){
cout<<bitFieldMap[1].size();
}
int main(){}
gives the following cryptic error
In function 'int recreate(const std::map > >&)':
D:\playground\testit.cpp:12:21: error: passing 'const std::map > >' as 'this' argument of
'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key,
_Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = int; _Tp = std::vector >; _Compare = std::less;
_Alloc = std::allocator > > >; std::map<_Key, _Tp,
_Compare, _Alloc>::mapped_type = std::vector >; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]' discards qualifiers [-fpermissive] cout<
whereas after removing const from recreate function it works well ie
int recreate( map <int , vector< string > > &bitFieldMap){
cout<< bitFieldMap[1].size() ;
}
int main(){}
In my understanding we use const when the value will remain unaltered signalling the compiler to make some optimizations.Now either the size() function used on the object changes some value each time it is executed or something eerie is going on with some memory allocated to map container upon calling size().
Now my problem can be solved by not using const here or using a multimap. But why does const and size show this behavior ?
You are not calling size() on the map. You are calling operator[] on the map, which is a non-const operation, since it will create an element at that position if one does not already exist.
You then attempt to call size() on the vector<string> at that position, but it is too late by this point. Incidentally, size() is const on Standard Library containers.
It actually isn't the size that is non-const it is the operator[].
Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.

Insert value into vector in a list

I have a specific list with vectors. i want to add a value to a vector. How can I do this?
Here is my code:
//creating the list with vectors
std::list< vector<string> > adjacencylist;
//adding some vectors to the list...
adjacencylist.push_back(std::vector<std::string>(1, "String"));
adjacencylist.push_back(std::vector<std::string>(1, "String"));
adjacencylist.push_back(std::vector<std::string>(1, "String"));
Now I want to add values to the vectors in the list...
I tried for this:
std::list< vector<string> >::const_iterator it = adjacencylist.begin();
(*it).push_back("Some more String");
I thought that this would work. So I could iterate over all vectors and insert the values that I want. But it does not work. Here the output of the compiler:
example.cpp: In function ‘int main(int, char**)’:
example.cpp:148:31: error: passing ‘const std::vector<std::basic_string<char> >’ as ‘this’ argument of ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::basic_string<char>; _Alloc = std::allocator<std::basic_string<char> >; std::vector<_Tp, _Alloc>::value_type = std::basic_string<char>]’ discards qualifiers [-fpermissive]
(*it).push_back("test");
Because you have declared it as a const_iterator you have made it such that what it references cannot be edited. When you call push_back you are attempting to edit the vector that it points to. You need to replace:
std::list< vector<string> >::const_iterator it = adjacencylist.begin();
(*it).push_back("Some more String");
with
std::list< vector<string> >::iterator it = adjacencylist.begin();
(*it).push_back("Some more String");
Read up on general constness here and there is more specific information on const_iterator here.
Use simply an iterator. Also you can use it -> push_back instead of (*it).push_back.
This is an example:
for (list< vector<string> > it = adjacencylist.begin(); it != adjacencylist.end(); ++it)
{
it -> push_back("Your std::string");
}

Iterator for a container of a custom object

If I've built a class that I want to contain inside, for example a set, how would I iterate through said set? Could I say
std::set<customObject>::iterator it
I thought I could do that, but I'm getting the following series of errors...
drawing.h:110: error: no match for ‘operator=’ in ‘it = ((object*)this)->object::objects. std::vector<_Tp, _Alloc>::begin [with _Tp = object, _Alloc = std::allocator<object>]()’
/usr/include/c++/4.2.1/bits/stl_tree.h:225: note: candidates are: std::_Rb_tree_const_iterator<object>& std::_Rb_tree_const_iterator<object>::operator=(const std::_Rb_tree_const_iterator<object>&)
drawing.h:110: error: no match for ‘operator!=’ in ‘it != ((object*)this)->object::objects. std::vector<_Tp, _Alloc>::end [with _Tp = object, _Alloc = std::allocator<object>]()’
/usr/include/c++/4.2.1/bits/stl_tree.h:292: note: candidates are: bool std::_Rb_tree_const_iterator<_Tp>::operator!=(const std::_Rb_tree_const_iterator<_Tp>&) const [with _Tp = object]
drawing.h:111: error: ‘struct std::_Rb_tree_const_iterator<object>’ has no member named ‘sketch’
here's my code:
void draw_in_place()
{
place();
std::set<object>::const_iterator it;
for(it = objects.begin(); it != objects.end(); it++){
*it.draw_in_place();
}
}
((object*)this)->object::objects. std::vector<_Tp, _Alloc>::begin
objects is apparently a std::vector<object>, not a std::set<object>. You therefore need to use a std::vector<object>::const_iterator.
*it.draw_in_place();
This is incorrect: you need to dereference the iterator to access the element first, then use the element:
(*it).draw_in_place();
// or
it->draw_in_place();
I think (at least) one of your problems is this line:
*it.draw_in_place();
The compiler is interpreting this as
*(it.draw_in_place());
versus your intended
(*it).draw_in_place();
To fix this, consider using the arrow operator, as in
it->draw_in_place();
It's perfectly legal to store custom objects in an STL set, so long as they can be compared with the < operator by default. If they can't, you'll either need to define operator < on them, or provide a custom comparator to the set, or specialize std::less for your particular type.

Problem with STL map iterator copying

I have an STL map that I want to iterate through, and can't seem to get the code to work. The code is:
//PowerupInfo is a struct defined in this class's header file
std::map<std::string, PowerupInfo> powerups;
...populate powerups
std::map<std::string, PowerupInfo>::iterator iter;
for (iter = powerups.begin(); iter != powerups.end(); iter++) {
return iter->second.type ;
}
The error message I get is:
error: no match for 'operator=' in 'iter = (((const std::map<std::string, PowerupInfo, std::less<std::string>, std::allocator<std::pair<const std::string, PowerupInfo> > >)((const PowerupList)this)) + 24u)->std::map<_Key, _Tp, _Compare, _Alloc>::begin with _Key = std::string, _Tp = PowerupInfo, _Compare = std::less<std::string>, _Alloc = std::allocator<std::pair<const std::string, PowerupInfo> >'|
note: candidates are: std::_Rb_tree_iterator<std::pair<const std::string, PowerupInfo> >& std::_Rb_tree_iterator<std::pair<const std::string, PowerupInfo> >::operator=(const std::_Rb_tree_iterator<std::pair<const std::string, PowerupInfo> >&)|
So I would normally assume that the problem has to do with setting iter equal to something it doesn't like, as it's not finding a match for 'operator='. But why? Why wouldn't that assignment work?
EDIT:
Turns out the method WAS const, causing the reference to powerups to be const as well, causing the error. I was just doing a bad job reading my own code. Thanks guys!
Your map name is poweruplist not powerups (You are using this name in the for loop). If this is not the cause of the error, then it looks like you are for loop is in a function which accepts the map by const reference (or is a const member function of a class). In that case your type of iterator should be const_iterator and not iterator.
Reformatting error code to make it readable:
error: no match for 'operator=' in
'iter =
((
(const std::map<std::string, PowerupInfo>*)((const PowerupList*)this)
)
+ 24u
)->std::map<std::string, PowerupInfo>::begin()'
Does not look the error message to the code you supplied.
Please cut and past the code. Otherwise it is meaningless.

C++: binary search compile error

I have the following lines of code:
if(std::binary_search(face_verts.begin(), face_verts.end(), left_right_vert[0]) &&
std::binary_search(face_verts.begin(), face_verts.end(), left_right_vert[1]))
And when I compile my code, I get the following errors:
In file included from /usr/include/c++/4.4/algorithm:62,
from R3Mesh.cpp:10:
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘bool std::binary_search(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<R3Point*, std::vector<R3Point, std::allocator<R3Point> > >, _Tp = R3Point]’:
R3Mesh.cpp:1335: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:2762: error: no match for ‘operator<’ in ‘__val < __i.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = R3Point*, _Container = std::vector<R3Point, std::allocator<R3Point> >]()’
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘_FIter std::lower_bound(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<R3Point*, std::vector<R3Point, std::allocator<R3Point> > >, _Tp = R3Point]’:
/usr/include/c++/4.4/bits/stl_algo.h:2761: instantiated from ‘bool std::binary_search(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<R3Point*, std::vector<R3Point, std::allocator<R3Point> > >, _Tp = R3Point]’
R3Mesh.cpp:1335: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:2442: error: no match for ‘operator<’ in ‘__middle.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = R3Point*, _Container = std::vector<R3Point, std::allocator<R3Point> >]() < __val’
make: *** [R3Mesh.o] Error 1
I did #include <algorithm> in the beginning of the file and I can't seem to figure out the error. The following are the containers used in the function call:
vector <R3Point > face_verts;
vector <R3Point > left_right_vert;
Thanks.
In order to use binary search, your items must be comparable. R3Point doesn't have built-in comparison, that's the core reason.
Moreover, for using binary_search your list must be already sorted wrt the comparison operation.
You need to implement an operator < for your R3Point class. The binary_search() function will use this operator to determine how to find the target item.
std::binary_search uses a predicate function to compare entries. That's operator < by default, so you need to overload this op for R3Point.
Keep in mind that the input range must be ordered by this op for std::binary_search to work properly (well, that's the nature of a binary search).
See http://www.sgi.com/tech/stl/binary_search.html.
In order to use binary_search you input siquence must be sorted in accordance with certain comparison predicate. Later, this very same comparison predicate must be given (explicitly or implicitly) to binary_search to be used during searching.
So, the questions you should answer in this case are the following
Is the input sequence sorted? If it is not, you can stop right here. binary_search cannot be used with unordered sequences.
If it is sorted, then what comparison predicate was used to sort it? And how was it passed to the sorting function?
Once you know the comparison predicate and the passing approach, you can do the same with binary_search.
Note, that the comparison is not necessarily implemented through the operator <, as other answers might suggest. It could be a standalone functor-based comparison predicate, for example. Moreover, the fact that binary_search did not pick up the comparison predicate automatically (as would be the case with operator <) suggests the "standalone" approach.
If the R3Point is implemented by you, then you can add operator< for it.
Otherwise, you must implement a comparison functor, and assign it to binary_search.
Remember the following mark:
Returns true if an element in the range [first,last) is equivalent to value, and false otherwise.