Why i need to use the const function in the Less Traits - c++

why i need to use the const function in the less traits?
for example, why i must use const in Age or ID member function.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
#include <set>
using namespace std;
class Person
{
public:
Person(int id, string name, int age):m_iID(id), m_strName(name), m_iAge(age){};
int Age() const {return m_iAge;}
int ID() const {return m_iID;}
void Display();
friend ostream& operator<<(ostream& out, Person& person);
private:
int m_iID;
string m_strName;
int m_iAge;
};
void Person::Display()
{
cout<<m_iID<<" "<<m_strName<<" "<<m_iAge<<endl;
}
ostream& operator<< (ostream& out, Person& person)
{
out<<person.m_iID<<" "<<person.m_strName<<" "<<person.m_iAge<<endl;
return out;
}
int SumPersonAge(int iSumAge, Person& person)
{
return iSumAge + person.Age();
}
template <typename Type>
void Display(Type t1)
{
cout<<t1<<endl;
}
class LessPerson
{
public:
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
};
int main()
{
set<Person, LessPerson> s1;
Person p1(1234, "Roger", 23);
Person p2(1235, "Razor", 24);
s1.insert(p1);
s1.insert(p2);
for_each(s1.begin(), s1.end(), Display<Person>);
}
if i remove the const the keyword in Age or ID function, the compiler will report me Error cannot convert 'this' pointer from 'const Person' to 'Person &'.

The answer is that the set will pass two const reference to Person objects to your comparator, and you cannot call a non-const function on a const variable.
You might be surprised as there seems not to be any const in the declaration of the functor:
struct LessPerson
{
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
};
But there is, it is just not explicit in the code. Inside the set implementation there are two const Person& references (call them r1, r2) and a LessPerson comparator (call it compare) and the code does something in the lines of if ( comparator(r1,r2) ). The compiler finds the templated operator() and tries to infer the types ending up with the type substitution: Type == const Person.
Why does the set use const references rather than plain modifiable references? Well, that is a different issue. The set is implemented as a sorted balanced tree, with each node containing the key. Now because a change in the key would break the order invariant, the keys are themselves const objects, ensuring that you cannot break the invariants.

That const keyword means that the function does not modify its object, this. Only const functions may be called from const objects. So, the compiler is telling you that you are trying to call a non-const member function from a const object.
You appear to be avoiding the const keyword, but it creeps in when the library calls your template function:
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
Type is passed as const Person.
const is not easy to get rid of without cheating, and it will always creep in. It's better to go with the flow and add const everywhere you take a reference that doesn't change an object (which is most of them).

Because the type passed to LessPerson is const Person.
So basically, LessPerson is roughly
class LessPerson   
{
public:
bool operator()(const Person& t1, const Person& t2)
{
return t1.ID() < t2.ID();
}
};
You can't call non-const methods on const objects. It's a rule of C++.

Related

How to Initialize a Map of Unique pointer Objects sorted by a Object Variable

Hello I am new to the c++ and have a problem with a Unique Pointer of a Object as a Key of a Map.
What does the template need to look like on std::map<std::unique_ptr<Person>,string,?> phonebookMap2;
so the Person gets Sorted/Inserted initial by first name?
Or how do i sort the map, i tired it with sort(phonebookMap2.begin(),phonebookMap2.end(),sortfunction2); but then ther is this issue:
no match for 'operator-' (operand types are
'std::_Rb_tree_iterator<std::pair<const std::unique_ptr,
std::__cxx11::basic_string > >' and
'std::_Rb_tree_iterator<std::pair<const std::unique_ptr,
std::__cxx11::basic_string > >')
i have class looking like this:
#ifndef PERSON_H
#define PERSON_H
#include ..
class Person
{
private:
string m_firstName;
string m_lastName;
string m_address;
public:
Person();
Person(const string& firstName, const string& lastName,
const string& address);
string getFirstName() const;
string getLastName() const;
string getAddress() const;
};
bool operator<(const Person& left, const Person& right){
return left.getFirstName() < right.getFirstName();
};
#endif // PERSON_H
Main:
#include...
bool sortfunction2(const std::unique_ptr<Person> &x,
const std::unique_ptr<Person> &y) { return x->getFirstName() < y->getFirstName(); }
int main()
{
//Template missing
std::map<std::unique_ptr<Person>,string,?> phonebookMap2;
phonebookMap2.insert(make_pair(std::make_unique<Person>("Max", "Mustermann", "Bahnstr. 17"),"06151 123456"));
phonebookMap2.insert(make_pair(std::make_unique<Person>("Hubert", "Kah", "Minnesängergasse 23"),"06151 654321"));
//Not working
sort(phonebookMap2.begin(),phonebookMap2.end(),sortfunction2);<br />
Your already provided an operator< for person, now all you need to do is tell your map to use that.
struct PersonPtrCmp
{
bool operator()(const std::unique_ptr<Person> &x,
const std::unique_ptr<Person> &y) const
{
return *x < *y;
}
};
std::map<std::unique_ptr<Person, std::string, PersonPtrCmp> phonebookMap2;
You cannot std::sort a std::map. Elements in a std::map are sorted and you cannot change order, that would break invariants of the map.
You can provide the comparison as functor. Then you only need to specify the functors type as argument of std::map:
struct PersonCompare {
bool operator()(const std::unique_ptr<Person>& left, const std::unique_ptr<Person>& right) const {
return left->getFirstName() < right->getFirstName();
}
};
int main() {
//Template missing
std::map<std::unique_ptr<Person>,string,PersonCompare> phonebookMap2;
phonebookMap2.insert(make_pair(std::make_unique<Person>("Max", "Mustermann", "Bahnstr. 17"),"06151 123456"));
phonebookMap2.insert(make_pair(std::make_unique<Person>("Hubert", "Kah", "Minnesängergasse 23"),"06151 654321"));
}
Complete Example
You will need to implement a type that meets the Compare requirements as documented here. You already have a comparison function defined, you'll just need to put it in a class that provides the appropriate operator().

How to properly rewrite with templates this C++ code that uses inheritance

I have a C++ code that currently looks like this: there is a class hierarchy to do perform some comparison and a list class that uses it. Which comparison operation to use is determined at runtime based on some schema object. Here is the structure:
class A{
bool doComparison(const string& s1, const string& s2) const=0;
}
class B: public A{
bool doComparison(const string& s1, const string& s2) const {
...
}
}
class C: public A{
bool doComparison(const string& s1, const string& s2) const {
...
}
}
template <class, S>
public FancyList{
shared_ptr<A> z_;
vector<S> v;
FancyList(shared_ptr<A> z) : z_(z);
void DoSmth(){
....
z_->doComparison(arg1, arg2);
}
}
typedef FancyList<string> FancyStringList;
// Determine which comparison to use at runtime
shared_ptr<A> c = nullptr;
switch(type):
case int:
c = make_shared<B>();
break;
case double:
c = make_shared<B>();
break;
FancyStringList l(c);
l.push_back("stuff");
C# used to be my main language so this code seemed ok to me. But I was told that the problem with this approach is that it uses virtual functions so there is a slight overhead in a method call. What is the proper C++-way of reorganizing this code so there is no need to have this class hierarchy and no need to use virtual functions?
Contrary to what you want, the overhead of virtual function is unavoidable because the decision of which actual function is called is made in runtime.
If the decision is always made in runtime, the compiler cannot hard-code the function call into the generated machine code. It has to be a indirect function call: to use a pointer to point to a function, and to dereference the pointer before the function call. Virtual function is just one way to do indirect function call.
Template is a way tell the compiler to generate code during compile-time. All template can do is to not introduce overhead when the decision is made during compile-time. It can't help you remove works that must be done in runtime.
If you are still interested in using template, you may consider having the comparator as a template parameter.
template <class T, class Comparator>
class MyList
{
std::vector<T> vec;
Comparator comp;
public:
void do_thing(const T& a, const T& b)
{
vec.push_back(a);
vec.push_back(b);
bool x = comp(vec[0], vec[1]); // for example
std::cout << x;
}
};
In the comparator class, overload the function call operator.
class Compare1
{
public:
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return lhs < rhs;
}
};
class Compare2
{
public:
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return lhs.size() < rhs.size();
}
};
int main()
{
MyList<std::string, Compare1> myli1;
myli1.do_thing("a", "b");
MyList<std::string, Compare2> myli2;
myli2.do_thing("c", "d");
}
You can even hide indirect function call behind comparator class. But it does not remove the overhead.
class A
{
public:
virtual bool doComparison(const std::string& s1, const std::string& s2) const=0;
virtual ~A() = default;
};
class PolymorphicComparator
{
private:
std::shared_ptr<A> comp;
public:
PolymorphicComp(std::shared_ptr<A> c) : comp(c) {}
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return comp->doComparison(lhs, rhs);
}
};

Use member functions directly with unordered_set

Is there a short-cut to using std::unordered_set with classes that implement operator== and hash? Specifically, is there a way to (1) avoid creating a stand-alone operator==(const Object& a, const Object& b) function, and (2) avoid defining an entire class just to hold size_t operator()(const Object& o) const {return o.hash();}
Of course, neither of these are problems, I'm just curious.
operator== being defined as a member function is already catered for.
If the class being used as a key has a member function hash() const then we can do something simple like this:
-
#include <unordered_map>
#include <string>
struct myclass {
std::size_t hash() const { return 0; }
bool operator==(const myclass& r) const { return true; }
};
struct self_hash
{
template<class T>
auto operator()(const T& r) const { return r.hash(); }
};
int main()
{
using mymap = std::unordered_map<myclass, std::string, self_hash>;
auto m = mymap();
}

Callback function: Incompatible argument

I am trying to use callback function in my problem but I got into some troubles. In the sort() function, the parameter &compareType has an error:
Argument of type "bool (Person::*)(const Person& p1, const Person& p2)" is incompatible with parameter of type "compare"`
person.h
class Person
{
public:
bool compareType(const Person& p1, const Person& p2) { return ... };
void sort()
{
...
list->addInOrder(person, &compareType);
...
}
...
}
dlinkedlist.h
typedef bool (*compare)(const Person& p1, const Person&p2);
class dlinkedlist
{
public:
void addInOrder(const Person& person, compare comparefunc)
{
Person person2;
...
comparefunc(person, person2);
...
}
}
bool compareType(const Person& p1, const Person& p2)
is actually of type
bool (Person::*) (const Person&, const Person&)
You have to make your method static to have correct type.
There are mainly three solutions.
You can either:
declare the member method as static
define a function outside the class that is friend of your class (if needed) and use it
The third solution is maybe the most interesting one:
you can use a non-capturing lambda function that, because of the fact that is a non-capturing one, can decay to a pointer to function.
So, as an example, the following lambda is perfectly fine in your case:
[](const Person& p1, const Person& p2) { return true; }
It follows a minimal, working example:
struct A { };
using Fn = bool(*)(const A &, const A &);
void f(Fn fn) {
fn(A{}, A{});
};
int main() {
f([](const A &, const A &){ return true; });
};
As you can see, the lambda automatically decays to a pointer to function, so it's fine to use it in such a case.
Obviously, the solution involving the lambda is not suitable if you need to access private members, of course.
A non-static method is different to a free function or static method. You can see that from the type in the error message:
bool (Person::*)(const Person& p1, const Person& p2)
which is different from the type of a simple function
bool (*)(const Person& p1, const Person& p2)
(intuitively, the non-static method has to somehow get a this pointer, so the compiler has to do something different when calling it).
Note that your compareType shouldn't be a non-static member anyway - you'd have to call it like
personA.compareType(personB, personC)
which doesn't make much sense.
Either make it a static method (so you don't invoke it on an instance of Person)
class Person {
public:
static bool compareType(const Person&, const Person&);
// ...
};
or just make it a free function
bool comparePeople(const Person&, const Person&);
Non static class method implicitly adds reference to this, so your function actually looks like
bool compareType(Person *this, const Person &p1, const Person &p2);
You should declare it as static, and this will not be passed into.

Hash function errors, c++

Here's the simple example class with hash function I wrote. Function maybe isn't most efficient one, but quality of hash function isn't relevant to me for now.
#include<iostream>
#include<unordered_set>
using namespace std;
class Class{
private:
int num;
public:
Class(int n){num=n;}
Class(){num=0;}
int getNum(){return num;}
friend bool operator==(const Class &k1, const Class &k2);
};
bool operator==(const Class &k1, const Class &k2){
return(k1.num == k2.num);
}
namespace std {
template <>
struct hash<Class>{
size_t operator()(const Class & c) const
{
return(31+c.getNum()*7);
}
};
}
void main(){
unordered_set<Class> set;
set.insert(Class(5));
set.insert(Class(55));
set.insert(Class(4));
set.insert(Class(123));
set.insert(Class(11));
for(unordered_set<Class>::iterator it = set.begin(); it!=set.end(); it++)
cout<<it->getNum()<< endl;
}
This worked fine when all fields of Class were public, but errors appear in hash function at return(31+c.getNum()*7); on c, because I can't call getNum() function. I don't know if there is any problem if in line size_t operator()(const Class & c) const I remove const, so object c is not const?
Also, in the last line, cout<<it->getNum()<< endl, there is error when accessing getNum() function. I don't know how to iterate over unordered_set set and to print num.
Your operator takes a const class but calls a non-const member function. To fix your issue declare getNum as const since it does not modify the class.
class Class{
private:
int num;
public:
Class(int n) : num(n) {}
Class() : num(0) {}
int getNum() const { return num;}
friend bool operator==(const Class &k1, const Class &k2);
};
In the above example also notice that I used member initializer lists to instantiate member variables. Also change the return type from void to int on main and I recommend either returning 0 or EXIT_SUCCESS from <cstdlib> at the end of main.