Non-static function as parameter to another function in C++ - c++

I've made a class student with subclass comparator. Constructor of comparator takes one argument called cmp_mode that specifies how we should compare students.
class student
{
public:
std::string name;
int course, group;
student(std::string name,
int course,
int group): name(name)
{
this->course = course;
this->group = group;
}
enum cmp_mode
{
NAME,
COURSE,
GROUP
};
class comparator
{
cmp_mode mode;
public:
comparator(cmp_mode mode)
{
this->mode = mode;
}
bool compare(student s1, student s2)
{
if (mode == NAME)
return s1.name < s2.name;
else if (mode == COURSE)
return s1.course < s2.course;
else if (mode == GROUP)
return s1.group < s2.group;
else
throw "Oh god! We can't compare variables using these keys!";
}
};
};
Also, I've created a list of students and now I want to sort this list using comparator subclass.
std::list<student> l;
student st1("Anya", 2, 5);
student st2("Misha", 4, 2);
student st3("Yasha", 1, 3);
l.push_back(st1);
l.push_back(st2);
l.push_back(st3);
student::comparator by_group(student::GROUP);
l.sort(by_group.compare);
But I'm getting the following error.
ERROR: Reference to non-static member function must be called.
So what should I do? How can I adjust sorting in the better way?

I'm starting from your comment:
I thought that I can write compare function for each case, but it seems even worse.
Why worse? Personally I think it is faster to run (at least faster to compile) and easier to maintain (shorter functions).
Isn't this much simpler to write:
l.sort(student::compare_by_group);
And this implementation easier to maintain:
class student
{
...
static bool compare_by_name(const student& s1, const student& s2)
{
return s1.name < s2.name;
}
static bool compare_by_course(const student& s1, const student& s2)
{
return s1.course < s2.course;
}
static bool compare_by_group(const student& s1, const student& s2)
{
return s1.group < s2.group;
}
// Add the followings only if you really need this stuff
// e.g. to get comparator type from elsewhere
enum cmp_mode
{
NAME,
COURSE,
GROUP
};
static bool compare(cmp_mode, const student& s1, const student& s2)
{
switch(cmp_mode) {
case NAME: return compare_by_name(s1, s2);
...
}
}
};

Like Piotr, I'd recommend to write comparators for each property, which is faster and less error-prone. However, I'd recommend to use functor objects instead of the static functions:
struct compare_by_name {
bool operator()(const student& a, const student& b) const {
return a.name < b.name;
}
};
If you need the comparators only once or maybe twice and you're using C++11, prefer an inline lambda:
l.sort([](const student& a, const student& b){ return a.name < b.name; });
If you absolutely need a dynamic comparator, write it as normal functor object, i.e., define an operator():
bool operator()(const student& a, const student& b) const {
switch (mode) {
case NAME:
return a.name < b.name;
// No default case, the constructor (if at all) should check whether the mode
// is valid.
}
}

Non-static member functions have an implicit parameter of the type of which the function is a member. They need an instance of this type in order to work. You can use std::bind (or boost.bind if you don't have C++11 support) to "bind" a student::comparator object to the first parameter of the comparison function:
student::comparator by_group(student::GROUP);
using namespace std::placeholders;
auto comp = std::bind(student::comparator::compare, &by_group, _1, _2);
l.sort(comp);
In the code above, the by_group object is bound as first argument to the student::comparator::compare function, and the resulting function object takes two student objects and returns a bool:
std::cout << std::boolalpha;
const student& s1 = l[0];
const student& s2 = l[1];
std::cout << comp(s1, s2) << "\n"; // prints "true" or "false".
I would also advise you to change the signature of the comparison member function to
bool compare(const student& s1, const student& s2) const;
There is no reason to pass by value, and there is no reason for the member function not to be const.

Related

Copying from map to a list of pointers

I have this interesting assignment where I have a std::map of CTurist (previous class) and unsigned variable. Here's the code:
class CTurist
{
protected:
string tName;
int age;
public:
CTurist() {};
CTurist(string name, int age2)
{
tName = name;
age = age2;
}
bool operator<(const CTurist& e) const
{
return age < e.age;
}
friend ostream& operator<<(ostream& os, const CTurist&& e);
friend ifstream& operator>>(ifstream& is, CTurist&& e);
};
class CHotel:public CTurist
{
protected:
string hName;
int stars;
int beds;
map<CTurist, unsigned> Turisti;
public:
unsigned Sum = 0;
CHotel(){};
CHotel(string hName2, int zvezdi, int legla)
{
hName = hName;
stars = zvezdi;
beds = legla;
}
int Compare()
{
list<CTurist*> list;
int br=0;
CTurist vyzrast;
map<CTurist, unsigned>::iterator it = Turisti.begin();
while (it != Turisti.end())
{
if (it->first < vyzrast)
{
br++;
}
else
{
list.push_back(std::move(&it->first));
}
}
}
};
I know it's quite a long one, but I thought it's best to give you all the information.
Now, the int Compare() function AT THE BOTTOM is the one causing me problems.
I have to check if the age of the tourists is above or below a parameter which I've called vyzrast here. We're comparing the age. If it's below, it's quite straight forward.
If it's above though, I have to add those Tourists to a list<CTurist*>, aka. to a list of pointers. If I make the list from objects, not pointers. No luck with this, hence why I'm looking for suggestions how to fix it here.
Why are you using rvalue references for your operator overloads? You should be using const CTurist & for operator<< and CTurist& for operator>>. And you should be using std::istream for operator>>:
friend ostream& operator<<(ostream& os, const CTurist &e);
friend istream& operator>>(istream& is, CTurist &e);
Aside from that, there is no reason for Compare() to use std::move() at all when populating the std::list, as it is adding pointers, not moving actual objects.
Compare() doesn't won't correctly for several reasons:
it->first is an actual const CTurist object, but your std::list is expecting CTurist* pointers. std::map keys are const, so &it->first is a CTurist const * pointer (non-const pointer to const object), not a CTurist* pointer (non-const pointer to non-const object) like you are expecting.
Your vyzrast object is uninitialized. You are not assigning any value to its age (and tName) member at all, so the result of your comparisons is indeterminate.
You are not incrementing your it iterator on each loop iteration, so you are stuck in an infinite loop if the std::map is not empty.
Try something more like this instead:
int Compare()
{
std::list<const CTurist *> clist;
int br = 0;
CTurist vyzrast("", SomeAgeValueHere);
std::map<CTurist, unsigned>::iterator it = Turisti.begin();
while (it != Turisti.end())
{
if (it->first < vyzrast)
{
br++;
}
else
{
clist.push_back(&it->first);
}
++it;
}
// use clist as needed...
return SomeValueHere;
}
Live Demo

Compare two objects in vector iteration C++

How to implement the comparison function in this case?
void remove(const Object1 &object1) {
for(auto &object2 : objectVector) {
if(object1 == object2) {
/*....... */
}
}
}
You are asking 2 questions:
How can objects be made comparable:
By implementing operator== for the class. Be sure you override operator != then too.
As a member function:
bool Object1::operator ==(const Object1 &b) const
{
// return true if *this equals b;
}
Or as a free function:
bool operator ==(const Object1 &a, const Object1 &b)
How can objects with a given value be removed from a vector:
The easiest way is to use std::remove:
objectVector.erase(std::remove(objectVector.begin(), objectVector.end(), object1), objectVector.end());
You can remove objects also while iterating through the vector, but you have to keep in mind that the vector iterator is invalidated then. You may not further iterate on the vector then.
An object of your class can contain different types and number of members so let's say that you have a class A:
class A{
int x,
float y;
char* z;
};
And you have two instances:
A a1;
A a2;
To compare:
if(a1 == a2) // compile-time error
;// DoSomeThing
Above you get the error because the compiler doesn't know which fields will be compared against each other. The solution is to overload the equals operator "==" to work on objects of your class.
class Student{
public:
Student(std::string, int age);
std::string getName()const;
int getAge()const;
bool operator == (const Student&);
private:
std::string name;
int age;
};
Student::Student(std::string str, int x) :
name(str), age(x){
}
bool Student::operator == (const Student& rhs){
return age == rhs.age;
}
std::string Student::getName()const{
return name;
}
int Student::getAge()const{
return age;
}
int main(){
Student a("Alpha", 77);
Student b("Beta", 49);
if(a == b)
std::cout << a.getName() << " is the same age as " <<
b.getName() << std::endl;
cout << endl << endl;
return 0;
}
Now the compiler knows how to compare objects of your class and know what the equals operator compares on your ojects members.
if object is a class you can override the operator == in the class as a friend function, or you can implement your own function bool isEqual(Object1 o1, Object1 o2) { if(something) return true; return false; }

Comparing the private member variables of class objects stored in a vector - C++

So I have created a program which can read a .dat file of about 20 lines containing info about different atoms (name, symbol, mass etc) and added them all to a vector of class type I made called Atom.
How would I write a function to find the atom with the highest mass?
Here is my class:
class Atom
{
string element, symbol;
float number;
float mass;
public:
Atom(string e, string s, float n, float m){
element = e; symbol = s; number = n; mass = m;
}
string getElement();
string getSymbol();
float getNumber();
float getMass();
float ratio();
friend ostream& operator<<(ostream& os, Atom c);
};
and the information is added to a vector with the following statements
ifstream fin("atoms.dat");
string E, S;
float M, N;
vector <Atom> periodic;
while(!fin.eof()){
fin >> E >> S >> M >> N;
Atom Atom(E, S, M, N);
periodic.push_back(Atom);
}
I want to be able to write a function which finds which atom has the highest mass, I've tried using a max_element function but I keep getting errors. Is there a quick way of comparing the member variables of class objects stored in a vector?
I'm currently using C++ 98 as it is what my course requires.
Thanks
I don't know what you did wrong with std::max_element, as you haven't provided what you've tried with it.
struct CompareAtomMass
{
bool operator()(const Atom& lhs, const Atom& rhs) {
return lhs.getMass() < rhs.getMass();
}
};
and then:
vector <Atom> periodic;
Atom max_atom = *max_element(periodic.begin(), periodic.end(), CompareAtomMax());
struct CompareAtomMass is called a function object. It's a class with operator() overloaded to return a bool. std::max_element requires just such a function object to spit out the max element as it needs a way to compare your Atoms.
EDIT:
You should mark your getter functions as const since they don't change the inner state of the class.
string getElement() const;
string getSymbol() const;
float getNumber() const;
float getMass() const;
This will allow you to call them from a const object of type Atom just as the above function object requires (const Atom&).
Variation of DeiDeis answer: If you only do this at one place, and don't feel a need to keep a CompareAtomMass function class, you can use a lambda:
const auto maxIt = max_element(periodic.begin(), periodic.end(),
[](const Atom& lhs, const Atom& rhs) {
return lhs.getMass() < rhs.getMass();
));
if(maxIt != periodic.end()){
// use *maxIt ;
}
in C++14 and later you can also use auto in lambdas:
const auto maxIt = max_element(periodic.begin(), periodic.end(),
[](const auto& lhs, const auto& rhs) {
return lhs.getMass() < rhs.getMass();
));
It is a good idea to make your member functions const.
This will allow this code. Otherwise just remove all const from my code.
In case your vector is empty, you will get null pointer.
struct AtomMassComparator
{
bool operator()(const Atom& lhs, const Atom& rhs)
{
return lhs.getMass() < rhs.getMass();
}
};
const Atom* getAtomWithHighestMass(const vector<Atom>& v)
{
vector<Atom>::const_iterator it = max_element(
v.begin(), v.end(), AtomMassComparator());
return v.end() == it ? 0 : &*it;
}

Sorting A Vector Of Structs According To Contained Data

I am sorry if there is similar questions already present on the website, but am currently failing to understand certain parts of the algorithm.
I have a Struct that contains information on user account information for my game:
struct Account
{
int Position;
string Name;
int Score;
string Date;
int Level;
bool operator < (User SOMETHING, User SOMETHING)
{
return (SOMETHING < SOMETHING);
}
};
vector<Account> User;
User.push_back(Account());
User.push_back(Account());
User.push_back(Account());
User[0].Position=1;
User[1].Position=2;
User[2].Position=3;
sort(User.begin(), User.end(), Account);
I need each struct of my vector to be to be organized, say for instance, in descending/ascending order for the "Position" value that each contains.
I just need help on (1) bool operator function (e.g. parameters and return values), and (2) How do I have it so that I can sort it by multiple variables like the positions, scores & level. (Would I need to have 3 bool operator functions?)
Use std::tie, something like this:
struct Account
{
int Position;
string Name;
int Score;
string Date;
int Level;
};
bool operator < (const Account& lhs, const Account& rhs)
{
return std::tie(lhs.Name,lhs.Score,lhs.Date) < std::tie(rhs.Name,rhs.Score,rhs.Date);
}
will sort according to Name first, if Name is equal then according to Score, when both Name and Score are equal then according to Date.
Sorting is simply done by:
std::sort(User.begin(), User.end());
Which, by default, uses operator< on the contained objects of type Account.
Update: I misunderstood your question. In your case you need separated comparators, e.g.
struct by_name_ascending
{
bool operator()(const Account& lhs, const Account& rhs) const
{
return lhs.Name < rhs.Name;
}
};
struct by_score_descending
{
bool operator()(const Account& lhs, const Account& rhs) const
{
return lhs.Score > rhs.Score;
}
};
and sort the vector with
std::sort(User.begin(), User.end(), by_name_ascending());
With lambdas, you could also use
std::sort(User.begin(), User.end(),
[](const Account& lhs, const Account& rhs){
return lhs.Name < rhs.Name;
}
);
directly, simply switching < and > for ascending/descending. No need for other helpers or operators in the class/struct itself.

Multiple Overloading of Operators

as you can see from the code I want to overload the < operator twice. 1 to sort by dist and the other by nodeID. I would like to check if there is any way to call the different overloaded methods. For example in the compLoc method, when I use the sort() method I want it to be sorted by nodeID but in other methods I want it to be sorted by dist.
struct AttSet{
int nodeID;
double dist;
bool operator < (const AttSet & str) const{
return (dist < str.dist);
}
/*
bool operator <(const AttSet & str){
return (nodeID < str.nodeID);
*/
bool operator == (const AttSet & str){
return nodeID == str.nodeID;
}};
void compLoc(Edge *edge, vector<Node*> &vertices){
int l = edge->length;
int vl = edge->head->nodeID;
int vr = edge->tail->nodeID;
/*
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end());
sort(vertices[vr]->attSet.begin(), vertices[vr]->attSet.end());
vector<AttSet> vInterSec;
set_intersection(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), vertices[vr]->attSet.begin(), vertices[vr]->attSet.end(), back_inserter(vInterSec));
*/}
You cannot have overloads that have the same signature. This holds for any function. How would you try to decide which version to use?
If you want sort the object based on different criteria you should use the sort version that takes a custom comparer function as the third argument.
Edit:
Of course you need to provide the comparer. I would suggest providing the comparers as static functions of the class if you have such power. This way you will not pollute enclosing namespace and you can access privates of the class with out exposing any getters. Since your properties are public the lambda would suffice, and probably be the best/cleanest approach.
Feeling adventurous I made a simple c++11 exercise program. For what it's worth, if you ever decided to go for proper encapsulation, I've shown both approaches:
#include <iostream>
#include <algorithm>
#include <vector>
#include <initializer_list>
#include <cassert>
using namespace std;
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v){
for(const auto& el : v){
out << el << '\n';
}
return out;
}
class A {
int a;
int b;
public:
A(std::initializer_list<int> l){
assert(l.size() == 2);
auto i = l.begin();
a = *i;
++i;
b = *i;
}
friend std::ostream& operator<<(std::ostream& stream, const A& e){
return stream << e.a << ' ' << e.b;
}
static bool compareViaA(const A& lhs, const A& rhs){
return rhs.a > lhs.a;
}
static bool compareViaB(const A& lhs, const A& rhs){
return rhs.b > lhs.b;
}
};
int main() {
std::vector<A> v {{2,3}, {3,2}, {1,4}, {4,1}};
//sort(v.begin(), v.end(), [](const A& a, const A& b){return a.a > b.a;}) // fails because of privacy violation
sort(v.begin(), v.end(), A::compareViaA);
std::cout << v << '\n';
sort(v.begin(), v.end(), A::compareViaB);
std::cout << v << '\n';
return 0;
}
Live: http://ideone.com/lDMujx.
I think you can implement this by using functor and take the comparator(operator< overload) outside the AttSet.
Here is a simple example:
struct AtrComparator {
bool distcmp;
AttrComparator(bool distcmp): distcmp(distcmp) {}
bool operator() (const AttSet &s1, const AttSet &s2) {
if(distcmp) {
return s1.dist < s2.dist;
} else {
return s1.nodeID < s2.nodeID;
}
}
}
And then you can do the sort through different feed, dist or nodeID.
.e.g:
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), AttComparator(true));
sort(vertices[vl]->attSet.begin(), vertices[vl]->attSet.end(), AttComparator(false));
You can't do that. They have the same signature exactly.
Use a functor or a lambda and pass it to whatever algorithm you want.
std::sort(std::begin(container), std::end(container),
[](const element_type& lhs, const element_type& rhs) { return ...; });
Another way to do this:
struct compare_by_node_id {
bool operator()(const AttSet& lhs, const AttSet& rhs) const {
return lhs.nodeID < rhs.nodeID;
}
};
struct compare_by_dist {
bool operator()(const AttSet& lhs, const AttSet& rhs) const {
return lhs.dist < rhs.dist;
}
};
And you could pass that to the algorithm like:
std::sort(std::begin(container), std::end(container), compare_by_node_id());
you cannot do that because compiler doesn't see difference between:
bool operator < (const AttSet & str) const; //this const doesn't allow to override any property of object(instance of AttSet) if I remember
and
bool operator < (const AttSet & str);
there're the same same return type, same parameter (same signature)
compiler cannot choose which one is better
There's not a great way to do this as far as I am aware, since the compiler will see these as the exact same and will throw an error. If you need to do this, use the < operator as whatever will occur the most often, and then write a method that you can call to compare two object. Something like this:
bool operator< (const Blah &blah) const {
return (most often operation)
}
bool Blah::other_operation(const Blah &blah) const {
return (other operation)
}