how to make find() function using vector (class) - c++

there is class like as
class C_Service
{
public :
C_Service(); {memset(this, 0, sizeof(*this));}
C_Service(int type, int idx) {memset(this, 0, sizeof(*this)); this->type = type; this->idx = idx;}
bool operator==(const C_Service& svc) const { return (this->type == svc.type && this->idx == svc.idx);}
word type;
word idx;
dword aId;
dword bId;
char* name;
};
I used test code as below,
void vec_find(int type, int idx)
{
vector<C_Service*> vec;
// added several items in vector vec
...
vector<C_Service*>::iterator iter;
C_Service cSvc(type, idx);
iter = find(vec.begin(), vec.end(), &cSvc);
C_Service* findsvc = *iter;
if(findsvc)
printf("FOUND : type(%d), idx(%d), name(%s)\n", findsvc->type, findsvc->idx, findsvc->name);
else
printf("Not FOUND!!\n");
}
then, it give "Not FOUND!!" even set correct value.
I found something wrong and trying change..
iter = find(vec.begin(), vec.end(), &cSvc);
to
iter = find(vec.begin(), vec.end(), cSvc);
remove "&"
then it give compile error message
/libcxx/algorithm: In instantiation of '_InputIterator
std::__1::find(_InputIterator, _InputIterator, const _Tp&) [with
_InputIterator = std::__1::__wrap_iter; _Tp = C_Service]':
no match for 'operator==' (operand types are 'C_Service*' and 'const
C_Service')
I searched that when I use find() function in Container, It can use operator==
but, I can't get a goal..T.T
What is my fault?

The problem is that your vec is a vector of pointers, not a vector of C_Service objects.
Thus
find(vec.begin(), vec.end(), &cSvc)
checks whether the address of the cSvc variable is contained within vec (which it's not because you just created cSvc so it can't be referenced from anywhere else). It does not use your operator== at all, it just compares pointers.
To fix it, you can either change vec to be a std::vector<C_Service> and do
find(vec.begin(), vec.end(), cSvc)
or pass a custom predicate to find_if, where you can dereference your pointers manually:
find_if(vec.begin(), vec.end(), [&](const C_Service *p) { return *p == cSvc; })

Related

passing const as this argument discards qualifiers only when using a unordered_map but not a vector

I understand that calling a non-const method to a constant object gives an error as explained here. This question, although deals with the same error, is not a duplicate because it is not about a non-constant method.
This time I have a minimal reproducible example:
// Example program
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
class Something
{
public:
int m_value;
Something(): m_value{0} { myVector.push_back(1); myMap["hello"]=3; }
void setValue(int value) { m_value = value; }
int getValue() { return m_value ; }
//int getValue(const int value){ return myVector[value] ; } //<-- this gives an error (just reference)
int getValue(const int value)const { return myVector[value]; }
//int getValue2(const std::string &name) {return myMap[name]; } //<--- this gives an error (just reference)
int getValue2(const std::string &name) const {return myMap[name]; } //HERE this gives an error (this question)
std::vector<int> myVector;
std::unordered_map<std::string,int> myMap;
};
int main()
{
const Something something{}; // calls default constructor
int l= something.getValue(0);
std::cout<<l<<std::endl;
l= something.getValue2("hello"); //<-- HERE the error
std::cout<<l<<std::endl;
return 0;
}
In comments there are two method declarations that illustrates the point about non-constant methods. I left them there for reference. This question is not above them.
You see the const getValue method that returns a vector element? This works without problem.
Now see the const getValue2 method that should return a unordered map element? Even though it is a constant method it generates an error
In member function 'int Something::getValue2(const string&) const':
17:68: error: passing 'const std::unordered_map<std::basic_string<char>, int>' as 'this' argument of 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = int; _Hash = std::hash<std::basic_string<char> >; _Pred = std::equal_to<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, int> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = int; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::basic_string<char>]' discards qualifiers [-fpermissive]
My question is: Why only with unordered maps passing constant as the index generates this error?
EDIT:
Thanks to the very useful answers. I modified the class to
// Example program
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
class Something
{
public:
int m_value;
Something(): m_value{0} { myVector.push_back(1); myMap["hello"]=3; }
void setValue(int value) { m_value = value; }
int getValue() { return m_value ; }
//int getValue(const int value){ return myVector[value] ; } //<-- this gives an error
int getValue(const int value)const { return myVector[value]; }
//int getValue2(const std::string &name) {return myMap[name]; } //<--- this gives an error
//this will generate an exception if name is not in the map
int getValue3(const std::string &name) const {
//return myMap[name];
return myMap.at(name);}
int getValue2(const std::string &name) const {
auto iter = myMap.find(name);
return (iter != myMap.end()) ? iter->second : 0;
}
std::vector<int> myVector;
std::unordered_map<std::string,int> myMap;
};
The const-qualified version of getValue2 only has const access to the members of Something. This means that it will see myMap with the type const std::unordered_map<std::string,int> and you cannot call any non-const member functions on myMap. The operator[] is a non-const member function (it cannot be made const, because it sometimes has to insert a value-initialized entry, namely when the key is not found in the map) so you get the error message about discarding qualifiers. To get around this, you can use .at(name) instead of [name]. This will throw an exception if name is not found in the map.
something is a const object, so getValue(int) and getValue2(string) need to be const-qualified in order to be callable on it. That means the this pointer inside of them will always be pointing at a const object, so all operations on the object's data members need to be const-qualified as well.
Something::getValue(int) calls myVector[value], which works OK because std::vector::operator[] has a const-qualified overload to provide read-only access to the elements of a const std::vector object.
On the other hand, Something::getValue2(string) calls myMap[name], which does not work because std::unordered_map::operator[] is not const-qualified at all, so it can't be called on a const unordered_map object. A std::(unordered_)map's operator[] performs an insertion operation if the specified key is not found, thus the std::(unordered_)map object can't be const when using operator[]. To read an element from a const std::(unordered_)map without inserting a new element, you have to use the map's find() method instead, which is const-qualified, eg:
int getValue2(const std::string &name) const {
auto iter = myMap.find(name);
return (iter != myMap.end()) ? iter->second : 0;
}
A std::vector either has an element at a given index or the index is out-of-bounds, in that case a call to its operator[] invokes undefined behavior.
On the other hand, with the key alone you cannot determine if a std::unordered_map has a value for that key or not. You have to search the map.
To find if a map has a value for a key and use that value:
std::unordered_map<int,int> x;
auto it = x.find(3);
if (it == x.end()) {
// element not found
} else {
auto value = it->second;
}
operator[] does more than that. If the element was not found it does insert a element with default constructed value for the given key and returns a reference to the newly inserted value. It is more or less the same as:
std::unordered_map<int,int> x;
auto it = x.find(3);
if (it == x.end()) {
it = x.emplace(std::make_pair(3,0).first;
// .second is bool to
// indicate wether an element
// was actually inserted.
// In this case we know it wasn't present before
}
auto value = it->second;
// or simpler:
auto value = x[3];
The benefit of operator[] is that you get a valid element in any case, but the price is that operator[] cannot be const.
TL;DR
Accessing a vector out-of-bounds is to be avoided. std::vector::operator[] always returns an existing element (or invokes undefined behavior). On the other hand, looking up elements in a std::unordered_map that are not present is common. In that case you need to choose what to do when the element was not present. One option is operator[] that potentially inserts an element in the map, hence cannot be const.

Iterate through a pointer set that contains string vectors in C++

This is my declaration of the set:
set< vector<string> >* tuples = new set< vector<string> >();
And this is how I am trying to iterate through it:
for(set< vector<string> >::iterator it = tuples->begin(); it != tuples->end(); it++){
if(it[column] == value){
rowResults->insert(*it);
}
}
but I get an error
no match for ‘operator[]’ (operand types are ‘std::set<std::vector<std::__cxx11::basic_string<char> > >::iterator {aka std::_Rb_tree_const_iterator<std::vector<std::__cxx11::basic_string<char> > >}’ and ‘int’)
if(it[column] == value){
^
You're applying [] to the iterator instead of to the object to which it points. You need to dereference the iterator (and mind operator precedence!):
for(set< vector<string> >::iterator it = tuples->begin(); it != tuples->end(); ++it){
if((*it)[column] == value){
rowResults->insert(*it);
}
}
Note that with iterators, it's better to use ++it instead of it++ in loops, since the latter can be less efficient under insufficient optimisation.
it is an iterator, not the vector object itself. To access the vector object just use *it
Even better: get rid of the confusing iterator type by defining a reference (here constant ref since we don't seem to need a non-const) to the element itself.
for(set< vector<string> >::iterator it = tuples->begin(); it != tuples->end(); it++){
const vector<string> &v = *it; // more readable
if(v[column] == value){
rowResults->insert(v);
}
}
as no decent C++ answer cannot not mention the "new" C++11, note that if you use -std=c++11 option, the syntax is much better to iterate on a list
for(auto v : *tuples)
{
if(v[column] == value){
rowResults->insert(v);
}
}
You may avoid iterator with something like:
std::set<std::vector<std::string>>
computeRowResult(const std::set<std::vector<std::string>>& input,
int column,
const std::string& value)
{
std::set<std::vector<std::string>> rowResults;
for (const auto& v : input) {
if (v[column] == value) {
rowResults.insert(v);
}
}
return rowResults;
}
or avoiding the manual loop with
std::set<std::vector<std::string>>
computeRowResult(const std::set<std::vector<std::string>>& input,
int column,
const std::string& value)
{
std::set<std::vector<std::string>> rowResults;
std::copy_if(input.begin(), input.end(),
std::inserter(rowResults, rowResults.end()),
[&](const auto& v) { return v[column] == value; });
return rowResults;
}
Demo

l-value specifies const object when using std::make_pair

struct MapInserter
{
private:
int count;
public:
explicit MapInserter()
: count(0)
{
}
std::pair<int, std::string> operator()(std::string& value)
{
return std::make_pair(count++, value);
}
};
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), map.begin(), inserter);
for (auto it = map.begin(), end = map.end(); it != end; ++it)
cout << it->first << " : " << it->second << endl;
return 0;
that's the code. VS returns a compile error regarding l-value specifies const object.
Clicking the error moves you to the following code in a file named utility
template<class _Other1,
class _Other2>
_Myt& operator=(pair<_Other1, _Other2>&& _Right)
{ // assign from moved compatible pair
first = _STD forward<_Other1>(_Right.first);
second = _STD forward<_Other2>(_Right.second);
return (*this);
}
at first, i've had the operator() take const std::string& so I removed the const, because it's obviously talking about the make_pair function. But it still hasn't gone away. Can anyone point me to what this error is about?
The problem is that std::transform() will try to assign to existing elements of the target container. Keys of a map are constant and cannot be assigned to, which is why you're getting a compiler error. But even if they were, you'd get undefined behavior at run-time here, because the target container is empty, and std::transform() would expect it to contain as many elements as the input range.
You should use std::inserter() to create an inserter iterator, like so:
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), std::inserter(map, map.begin()), inserter);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is a live example.
Moreover, taking the value string by mutable lvalue reference in the call operator of your MapInserter is not a good idea: you don't want the argument to be modified, so you should either take it by const& or - my advice - take it by value and then move it into the returned pair, like so:
std::pair<int, std::string> operator()(std::string value)
{
return {count++, std::move(value)};
}
Since std::pair's constructor is not explicit, you do not even need the call to std::make_pair() in this case.

Getting object within vector based on object pointer

I'm trying to iterate through a vector of Player objects and then return the next one based off a Player pointer. I have tried various ways to get the operators within the lambda to match but cannot get it to work and have read similar such issues on stack overflow. I'm sorry for not making a SSCCE, I just don't know how at my current skill level.
Source of c_player:
Defined as:
Player* current_player;
Set with:
void Game::set_first_player_turn(){
current_player = &game_players.back();
}
Erroneous code:
using namespace std;
vector<Player>game_players;
Player* Game::find_player(Player* c_player){
vector<Player>::iterator iter;
iter = find_if(game_players.begin(), game_players.end(),
[&](Player* p ) -> bool{ return p == c_player; }); //This line
// causes the exception. Sets iterator to
// position of the current player
if (iter != game_players.end()){
advance(iter, 1);
}
else{
iter == game_players.begin();
}
current_player = &(*iter);
return current_player;
}
Compile error:
error C2664: 'bool Game::set_game_flow::<lambda_360ac8a355100cfac1adc9f8eba8d8b9>
::operator ()(Player *) const' : cannot convert parameter 1 from 'Player'
to 'Player *'
Thanks for any help.
Predicate in find_if has to take a const reference to a type stored in container. You store a Player type in vector
vector<Player> game_players;
Thus
[&](Player* p )
is not a correct predicate. This should be
[c_player]( const Player& p ) { return p == *c_player; }
// ^^^^^^^^^^^^^^
// to compare by value
// note: to compare by value
// bool operator==( const Player&, const Player&)
// must be defined
[c_player]( const Player& p ) { return &p == c_player; }
// ^^^^^^^^^^^^^^
// to compare by address
// note: address of c_player
// has to be valid (not invalidated by vector through resizing, erasing, etc)
So you can write:
vector<Player>::iterator iter;
iter = find_if( game_players.begin(), game_players.end(),
[c_player]( const Player& p ) { return p == *c_player;});

No match for operator== in generic function

I have the code below, I'm attempting to write a generic function which takes 2 iterators and an object and checks for any occurrences and returns the number of occurrences.
below my simple class
class person{
string name;
int age;
public:
person(string n, int a): name(n), age(a) {}
bool operator==(person &p);
};
bool person::operator==(person &p){
return (name == p.name && age == p.age);
}
Below is the generic function
template<typename Iter, typename Obj>
int count_obj(Iter iter1, Iter iter2, Obj &obj){
int count = 0;
for (; iter1 != iter2; iter1++){
if((*iter1) == obj)
count += 1;
}
return count;
}
my main:
int main(){
vector<person *> myp;
person a("ted", 21); person b("sun", 100); person c("ted", 21);
myp.push_back(&a);myp.push_back(&b);myp.push_back(&c);
cout<< "occurences for person objects " << count_obj(myp.begin(), myp.end(), a) << '\n';
}
Full error
3b.cc: In function ‘int count_obj(Iter, Iter, Obj&) [with Iter = __gnu_cxx::__normal_iterator<person**, std::vector<person*> >, Obj = person]’:
3b.cc:61:79: instantiated from here
3b.cc:42:3: error: no match for ‘operator==’ in ‘iter1.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = person**, _Container = std::vector<person*>, __gnu_cxx::__normal_iterator<_Iterator, _Container>::reference = person*&]() == obj’
make: *** [3b] Error 1
I cant seem to figure out I'm getting this error.
You have a vector of person *, and you're trying to compare them against a person. You will need to modify the line of code in count_obj to be either:
if (*(*iter1) == obj)
or:
if ((*iter1) == &obj)
depending on whether you wish to compare pointers or objects.
[Note: Are you aware of the std::count function in the standard library?]
[Note (2): As mentioned in another answer, you should probably read up on "const correctness". You should declare your operator== as const, and it should take a const reference as an argument.]
[Note (3): Storing raw pointers in a container is often a bad idea. For instance, are you aware that you effectively have a memory leak?]