C++: vector containers and <algorithm> std::find - c++

I created a struct that contains information about variables, namely their name and number
struct var{
string name;
int value;
};
Now, I want to use iterators to update it, using the following function:
void updateVariable(vector<Variable>& vars,Variable& newVar){
vector<Variable>::iterator it = find(vars.begin(), vars.end(), newVar);
if(it == vars.end()){
vars.push_back(newVar);
}
else{
*it = newVar;
}
}
Just to be sure, the error I'm getting is at the line with the call to find(). Any ideas why I'm getting the error? Here is the error:
/usr/include/c++/4.6/bits/stl_algo.h:162:4: error: no match for ‘operator==’ in ‘__first.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = Variable*, _Container = std::vector<Variable>, __gnu_cxx::__normal_iterator<_Iterator, _Container>::reference = Variable&]() == __val’
Update:
Thank you guys for all the quick help, and the clear answers!

You haven't defined operator == for your var struct. The find() algorithm by default will use operator == to compare the value you provide with the values in the specified range, and return the iterator to the first element which compares equal.
To fix this, just overload operator == for your class. One way to do that is this:
struct var
{
string name;
int value;
};
bool operator == (var const& v1, var const& v2)
{
return (v1.name == v2.name) && (v1.value == v2.value);
}
Make sure you define operator == in the same namespace as your var structure, otherwise ADL (Argument Dependent Lookup) will fail and you will likely get a compiler error.
If you are working with C++11 and you do not want to bother defining an overloaded version of operator ==, you can even use find_if() and pass a lambda as the last argument:
find_if(vars.begin(), vars.end(), [&] (var const& v) {
return (v.name == newVar.name) && (v.value == newVar.value);
});
As GManNickG correctly points out, when you need to compare several members, using std::tie and the overloaded operator == for std::tuple may save you some typing:
auto const tieMembers = [] (const var&) {
return std::tie(v.name, v.value, ...);
};
The above lambda can then be used this way when comparing values v1 and v2 of type var:
return (tieMembers(v1) == tieMembers(v2));

You need to define an overload of operator== for var. This should work.
bool operator==(const var& a, const var& b){
return (a.name == b.name) && (a.value == b.value);
}

Related

qsort of a structure in c++

I try to sort a structure but I have this error :
error: cannot convert ‘std::vector<Node>’ to ‘void*’ for argument ‘1’ to ‘void qsort(void*, size_t, size_t, __compar_fn_t)’
qsort(nodes,nodes.size(), sizeof(Node), dataClustering::compare);
This is my code :
compare function :
int compare(const void * node1, const void * node2){
string name1 = ((const struct Node*)node1)->name;
string name2 = ((const struct Node*)node2)->name;
int start1 = ((const struct Node*)node1)->start;
int start2 = ((const struct Node*)node2)->start;
if(name1 <= name2 && start1 <= start2){
return -1;
}
else if(name1 > name2 && start1 > start2){
return 1;
}
else{
return 0;
}
}
Call of compare in another function :
qsort(allNodes,allNodes.size(), sizeof(Node), compare);
And the header in .hpp file (I try to put it in static but this don't solve the error):
int compare(struct Node *, struct Node *);
You can pass vector to C-style sort, for example, like this:
qsort(&allNodes[0], allNodes.size(), sizeof(Node), compare);
You would be better off using std::sort, which works with vectors natively:
bool compareNodes(const Node& lhs, const Node& rhs) {
return (lhs.name < rhs.name)
|| (lhs.name == rhs.name && lhs.start < rhs.start);
}
...
std::sort(allNodes.begin(), allNodes.end(), compareNodes);
in C++11/C++14 you can define comparison as a lambda, too:
std::sort(allNodes.begin(), allNodes.end(), [] (const auto& lhs, const auto& rhs) {
return (lhs.name < rhs.name)
|| (lhs.name == rhs.name && lhs.start < rhs.start);
});
(the code above uses auto for lambda arguments, a C++14 feature).
The error is that you
cannot convert ‘std::vector<Node>’ to ‘void*’
and thus, a std::vector<Node> cannot possibly be a suitable first argument to a function like qsort with signature
void (void*, size_t, size_t, __compar_fn_t)
While there are things you could do to use qsort here, you really shouldn't: you should use std::sort from the C++ standard library:
using std::sort;
sort(begin(allNodes), end(allNodes), compare_function);
Here, compare_function should be a function that acts like < and takes two arguments of the appropriate type (e.g. the actual type of the objects you're sorting or const references to them, not pointers to them or void pointers) and returns bool if the first argument is less than the second. (compare_function doesn't actually have to be a function: it can also be any object that has an appropriate operator() or a lambda)
If you want to just sort, use
#include <algorithm>
...
bool myfunction (const Node &lhs, const Node &lhs) {
if(lhs.name < rhs.name) return true;
return (lhs.name == rhs.name && lhs.start < rhs.start));
}
...
std::sort(allNodes.begin(), allNodes.end(), compare_func);
Further read: std::sort
If you want to use cstyle qsort(which I suggest you should not), add a wrapper function to do the conversion of void * to Node * and change the first argument of qsort to static_cast<void *>(&allNodes[0])

Vector of structs - not seeing my definition for operator==

I have a class called Something that has two things: a string, and a vector of instructions. In that class, I want to define operator==. However, I get an error when I try to compile:
error: no match for ‘operator==’ in ‘* __first1 == * __first2’
This happened at the line where I am comparing the two vectors in Something using == (since vector has that conveniently defined, I would like to use it).
instruction is as follows:
struct instruction
{
int instr;
int line;
bool operator==(const instruction& rhs)
{
return (instr == rhs.instr) && (line == rhs.line);
}
};
I've searched for a solution to no avail. It seems that vector from the STL is not seeing the operator== I have defined for my struct when it's comparing these elements.
You haven't shown the code that's actually failing, but most likely is a scenario such as this:
int main()
{
vector <instruction> ins;
vector <instruction>::const_iterator itA = /*...*/, itB = /*...*/;
bool b = (*itA == *itB);
}
In this case, the problem is the fact that operator== is not const. Change the declaration as follows:
bool operator==(const instruction& rhs) const
^^^^^^^
Try to add qualifier const to the operator ==.
Also you did not show how the vector is declared and used.
You probably want to make the operator=() method itself const. You do that by adding 'const':
struct instruction
{
int instr;
int line;
bool operator==(const instruction& rhs) const // add const keyword here
{
return (instr == rhs.instr) && (line == rhs.line);
}
};

Polymorphism,STL,find and operator==

I ran into a problem.
I have a class A,and a class that inherits from A,lets call it class B.
I have virtual functions.
I want to compare A and B to another class C by operator==.
If i want to have a list of A's,lets say in stl list,
I must use a pointer to A,so it will look like:
list<*A> list;
and also i have: C something
but now,i cant use the function:find(list.begin(),list.end(),something)
because i cant use operator == for pointers(*).
I found a solution but i dont think its the best,so my question is-can i do it better?
iter=list.begin();
for(iter;iter!=list.end();++iter)
{
if((*iter).operator==(something)
return ...
}
Thank you.
You could use find_if, which lets you provide a function to check for equal values.
auto check_something =
[&something](const list<*A>::iterator& iter){return *iter == something; };
find_if(list.begin(),list.end(),check_something)
You can use
if(**iter == something)
if you want to dereference the pointer.
In C++1x, there is also
for(auto ptr : list)
if(*ptr == something)
Nothing says you can't make a global non-member operator == that operates on pointers or combinations of pointers and objects. If you have many types you could template the combination of pointer vs object equality for any type.
Edit to add this tip: Put the comparison in a namespace with your objects and then argument dependent lookup will find it without putting a global T* == T in scope that catches everything:
namespace equals {
struct A {
A(int x) :x(x) { }
bool operator == (const A &other) const {
return other.x == x;
}
int x;
};
template <typename T>
bool operator == (const T *lhs, const T &rhs) {
return *lhs == rhs;
}
template <typename T>
bool operator == (const T &lhs, const T *rhs) {
return lhs == *rhs;
}
}
Now you can do things like:
equals::A b(1), c(1);
if (b == &c) std::cerr << "good!" << std::endl;
You might have a look at boost::indirect_iterator which is designed for just this purpose.
find(
boost::make_indirect_iterator( list.begin() ),
boost::make_indirect_iterator( list.end() ),
something );

operator== overloading considering non-static member function

I have defined a class like this
using namespace std;
class foo {
public:
typedef std::pair< int, int > index;
bool operator == ( const index &l, const index &r )
{
return (l.first == r.first && l.second == r.second);
}
void bar()
{
index i1;
i1.first = 10;
i1.second = 20;
index i2;
i2.first = 10;
i2.second = 200;
if (i1 == i2)
cout << "equal\n";
}
};
However I get this error in windows
error C2804: binary 'operator ==' has too many parameters
and this error in linux
operator==(const std::pair<int, int>&, const std::pair<int, int>&)’ must take exactly one argument
I found this topic overloading operator== complaining of 'must take exactly one argument' and seems to be a problem with static and non-static functions in a class. However I don't know how to apply this
For example, this is not correct
bool operator == ( const index &r )
{
return (this->first == r.first && this->second == r.second);
}
How can I fix that?
The operator== can be implemented in two ways:
As member function: in this case, the function takes one argument and is invoked on the left operand which is passed as this pointer implicitly to the function.
As non-member function, in which case, the function takes two arguments, left and right operands.
Since you're implementing operator== for std::pair, you cannot implement it as member function (of std::pair). The option you're left with is non-member function.
So implement it outside the class as:
bool operator==(std::pair<int,int> const & l, std::pair<int,int> const & r)
{
return (l.first == r.first && l.second == r.second);
}
But then you don't really need to implement it yourself unless you want to implement it differently. The Standard Library has already provided a generic version of operator== for std::pair which lexicographically compares the values in the pair, like I did above, i.e compare first with first and second with second. If you need to compare them differently, only then provide your own specific definition (non-template version).
The above mentioned points are worth noting as to how to implement operator== when you need it for your defined types.
You need to move operator== out of class foo:
bool operator == ( const foo::index &l, const foo::index &r )
{
return (l.first == r.second && l.second == r.second);
}
class foo {
public:
typedef std::pair< int, int > index;
void bar()
{
index i1;
i1.first = 10;
i1.second = 20;
index i2;
i2.first = 10;
i2.second = 200;
if (i1 == i2)
cout << "equal\n";
}
};
Also note, std::pair has overload operator== already, see: link, you might reconsider if necessary to write your own again.
If you overload the == operator inside a class, it should only take a single parameter so that comparison can be done between the current object and the argument.
You can move that operator out of the class, that way you can take 2 operands. Indeed there is no point of keeping it within the class at this point, since you are only comparing member variables and not the class itself.
Indeed I wont be surprised if pair already defines the operator you write.
Edit : Yup It looks like pair already implements this
Two pair objects are compared equal if the first elements in both objects compare equal to each other and both second elements also compare equal to each other - they all have to match.
p.s. I think you meant
return (l.first == r.first && l.second == r.second);
^^^^^^

How can I make find() work with a set of structs?

I am using a set to hold structs which contain several strings. I want to be able to use the find() functionality of sets. However, since the set is holding structs, it doesn't work. I want find() to look only at one of the strings in the struct. How can this be done?
Here's the code that I tried to use. It works fine except for the part where find() is used:
#include <iostream>
#include <string>
#include <set>
using namespace std;
struct test
{
string key;
string data;
};
bool operator<(const test & l, const test & r)
{
return l.key < r.key;
}
bool operator==(const test & l, const test & r)
{
return l.key == r.key;
}
set<test> s;
int main()
{
test newmember;
newmember.key = "key";
newmember.data = "data";
s.insert(newmember);
s.find("key");
}
Here are the error messages that I get when I try to compile it:
test.cpp:30:7: error: no matching member function for call to 'find'
s.find("key");
~~^~~~
In file included from test.cpp:3:
In file included from /usr/include/c++/4.2.1/set:65:
/usr/include/c++/4.2.1/bits/stl_set.h:429:7: note: candidate function not viable: no known conversion from 'const char [4]' to 'const key_type' (aka 'const test') for 1st argument
find(const key_type& __x)
^
/usr/include/c++/4.2.1/bits/stl_set.h:433:7: note: candidate function not viable: no known conversion from 'const char [4]' to 'const key_type' (aka 'const test') for 1st argument
find(const key_type& __x) const
^
1 error generated.
I suggest you operator< and operator== to your struct instead of overloading the global operator, I find it much cleaner; example:
struct test
{
string key;
string data;
bool operator<(const test& rhs) const
{
return key < rhs.key;
}
bool operator==(const test& rhs) const
{
return key == rhs.key;
}
};
Now on to your real problem - your are passing a string to the find() function, but it only accepts structs of type test. In order to do so, add a constructor for automatic conversion, so the final struct would look like this:
struct test
{
string key;
string data;
test(const std::string& strKey = "", const std::string& strData = "")
: key(strKey),
data(strData) {}
bool operator<(const test& rhs) const
{
return key < rhs.key;
}
bool operator==(const test& rhs) const
{
return key == rhs.key;
}
};
Then passing a string to find() would automatically call the constructor and create a temporary test struct containing only the relevant key. Note that in this special case, the constructor must not be declared explicit.
To be able to put your structs into set you have to specify operator< for your struct. You can make the operator< return result from comparing corresponding string members.
To be able to use find you can specify operator== for your struct to return true if corresponding string members are equal.
Sample:
// code from your question used here
int main()
{
test newmember;
newmember.key = "key";
newmember.data = "data";
test findMember;
findMember.key = "key";
// as operator== and operator< doesn't care about data field we can left it be
// initialized by default constructor
s.insert(newmember);
s.find(findMember);
}
If you want to call find() with string parameter you can provide an implicit constructor from string for your test struct for example like this:
struct test {
//...
test(const string &in_key) : key(in_key) {}
//...
};
But usage of implicit constructors isn't a good technique, because it can lead to some unpredictable conversions somewhere further in your code.
First and foremost, in order to make std::set::find() work with your struct, you don't need to specify operator==, as explained for std::set:
[...] two objects a and b are considered equivalent if neither compares less than the other: !comp(a, b) && !comp(b, a).
However, your actual problem is, that you can't search for parts of your struct by using find() prior to C++14. Prior to C++14, find() expects an element as argument, whose type matches the type of elements stored in the set. For your case, this means that you have to provide an instance of test. However, as your operator< compares only the key variables, you can use a dummy value for the data variable, for example, as follows:
test newmember;
newmember.key = "key";
newmember.data = "data";
s.insert(newmember);
auto it = s.find(test{ "key", "" }); // "" is the dummy variable.
std::cout << it->key << ", " << it->data << std::endl;
Output:
key, data
Please be aware that your operator< which compares only the key variables has a side effect: Two instances of test can only be stored in the set if their key variables are different, even if their data variables are different. For example, if you append the following code to the code above, then newmember will not be inserted (again):
newmember.data = "otherdata";
s.insert(newmember); // Insertion fails!
for (auto const& t : s) {
std::cout << t.key << ", " << t.data << std::endl;
}
Output:
key, data
Consequently, if you want to store multiple elements with the same key, then you might have to choose a different container.
Anyway, if a set of struct is fine for you and you can make use of C++11, then you can also use a lambda expression
instead of defining operator< for your struct:
auto comp = [](const test& t1, const test& t2) { return t1.key < t2.key; };
std::set<test, decltype(comp)> s(comp);
Code on Ideone
C++14
Since C++14, you can use find() to do a "transparent comparison" as explained on std::set::find().
This means, that you can find an element that compares equivalent to the given argument.
In detail this means, that you have to define operator< and your set as follows:
bool operator<(const test& t, const std::string& str) { return t.key < str; }
bool operator<(const std::string& str, const test& t) { return str < t.key; }
bool operator<(const test& t1, const test& t2) { return t1.key < t2.key; }
std::set<test, std::less<>> s;
Then you can perform your find() operation as you would have expected it to work:
test newmember;
newmember.key = "key";
newmember.data = "data";
s.insert(newmember);
auto it = s.find("key");
std::cout << it->key << ", " << it->data << std::endl;
Output:
key, data
Code on Ideone
Only you have to override operator<() function to enhance find function.
In your case simply replace operator<() function as..
bool operator<(const offer& t1) const {
if(this->key< t1.key)
return true;
else if(this->key == t1.key){
if(this->data < t1.data)
return true;
else
return false;
}
else
return false;
}