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);
}
};
Related
class Media {
public:
bool operator==(const Media& other) const {}
bool operator!=(const Media& other) const {}
};
class Book : public Media {
public:
bool operator==(const Book& other) const {} // commenting out this line solves this issue.
bool operator!=(const Book& other) const {}
};
class Game : public Media {
public:
bool operator==(const Game& other) const {}
bool operator!=(const Game& other) const {}
};
int main() {
Book book;
Game game;
bool res = book == game; // doesn't compile.
}
I have these 3 classes and they must have their own == and != operators defined. But then I also have to compare between two siblings using those operators.
I could've written a (pure) virtual function, say, virtual bool equals(const Media& other) const in the base class that subclasses override. And then call that function in the bodies of == and != opertor definition in base class Media. But that feature is gone when I add another bool operator==(const Book& other) const {} in the Book class (the same goes for the Game class too).
Now I want to compare between siblings using those operators and still have all 6 definition in those 3 classes. How do I make it work?
You mentioned in the comments that this form of comparison is an imposed restriction (to compare among siblings of a child type). If its an imposed restriction that you need to somehow perform this with inheritance, then one option is to fulfill the base signature and use dynamic_cast. Note that this is not a clean approach, but it might be the expected solution for this problem if this is some form of assignment.
dynamic_cast uses Runtime Type Information (RTTI) to determine whether an instance to a base class is actually an instance of the derived class. When you use it with a pointer argument, it returns nullptr on failure -- which is easily testable:
auto p = dynamic_cast<const Book*>(&other);
if (p == nullptr) { // other is not a book
return false;
}
// compare books
You can use this along with a virtual function to satisfy the hierarchy. However, to avoid possible ambiguities with c++20's generated symmetric operator==/operator!= functions, it's usually better to do this through a named virtual function rather than the operator== itself in order to prevent ambiguity:
class Media {
public:
virtual ~Media() = default;
bool operator==(const Media& other) const { return do_equals(other); }
private:
virtual bool do_equals(const Media& other) const = 0;
};
class Book : public Media {
...
private:
bool do_equals(const Media& other) const override {
auto* p = dynamic_cast<const Book*>(&other);
if (p == nullptr) { return false; }
return (... some comparison logic ...);
}
...
};
... Same with Game ...
Since we never define operator==(const Book&) or operator==(const Game&), we won't see this shadow the base-class' operator==; instead it always dispatches through the base's operator==(const Media&) -- which is non-virtual and prevents ambiguity.
This would allow a Book and a Game to be comparable, but to return false -- whereas two Book or two Game objects may be compared with the appropriate logic.
Live Example
That said...
This approach is not a good design, as far as software architecture goes. It requires the derived class to query what the type is -- and usually by the time you need to do this, that's an indication that the logic is funky. And when it comes to equality operators, it also leads to complications with symmetry -- where a different derived class may choose to compare things weirdly with different types (imagine a Media that may compare true with other different media; at which point, the order matters for the function call).
A better approach in general is to define each of the respective equality operators between any types that logically require equality comparison. If you are in C++20 this is simple with symmetric equality generation; but pre-C++20 is a bit of a pain.
If a Book is meant to be comparable to a Game, then define operator==(const Game&) or operator==(const Book&, const Game&). Yes, this may mean you have a large number of operator==s to define for each of them; but its much more coherent, and can get better symmetry (especially with C++20's symmetric equality):
bool operator==(const Game&, const Book&);
bool operator==(const Book&, const Game&); // Generated in C++20
bool operator==(const Game&, const Game&);
bool operator==(const Book&, const Book&);
In an organization like this, Media may not even be logical as a 'Base class'. It may be more reasonable to consider some form of static polymorphism instead, such as using std::variant -- which is touched on in #Jarod42's answer. This would allow the types to be homogeneously stored and compared, but without requiring casting from the base to the derived type:
// no inheritance:
class Book { ... };
class Game { ... };
struct EqualityVisitor {
// Compare media of the same type
template <typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; }
// Don't compare different media
template <typename T, typename U>
bool operator()(const T&, const U&) const { return false; }
};
class Media
{
public:
...
bool operator==(const Media& other) const {
return std::visit(EqualityVisitor{}, m_media, other.m_media);
}
private:
std::variant<Book, Game> m_media;
};
Live Example
This would be my recommended approach, provided the forms of media are meant to be fixed and not extended.
You might do double dispatch thanks to std::visit/std::variant (C++17):
class Media;
class Book;
class Game;
using MediaPtrVariant = std::variant<const Media*, const Book*, const Game*>;
class Media {
public:
virtual ~Media () = default;
virtual MediaPtrVariant asVariant() const { return this; }
};
class Book : public Media {
public:
MediaPtrVariant asVariant() const override { return this; }
};
class Game : public Media {
public:
MediaPtrVariant asVariant() const override { return this; }
};
struct EqualVisitor
{
template <typename T>
bool operator()(const T*, const T*) const { return true; }
template <typename T, typename U>
bool operator()(const T*, const U*) const { return false; }
};
bool operator ==(const Media& lhs, const Media& rhs)
{
return std::visit(EqualVisitor(), lhs.AsVariant(), rhs.AsVariant());
}
bool operator !=(const Media& lhs, const Media& rhs)
{
return !(lhs == rhs);
}
int main()
{
Book book;
Game game;
bool res = book == game;
}
Demo
Given the class
class objects {
public:
bool compareArea (const objects& obj) const { return this->area < obj.area; }
private:
double area;
};
I want to sort a
list<shared_ptr<objects>> myObjects;
I cannot use a lambda (since my toolchain's C++11 support is incomplete). Thus, I tried the following:
using namespace placeholders;
myObjects.sort(bind(&objects::compareArea,_1,_2));
This line is called from another file (not from a class member!). The problem is, that compareArea requires two objects as input. But I give two shared pointer to objects to it. Is there an easy way of how to include the dereferencing of the pointers into the sort-call? I want the objects::compareArea(..) function to stay as it is. I do not want this kind of solution
bool compareArea (const shared_ptr<objects>& ptr1, const shared_ptr<objects>& ptr2) {
return ptr1->area > ptr2->area;
}
// in same source-file:
myObjects.sort(bind(compareArea,_1,_2));
where compareArea is no member-function of objects. Actually an operator overloading of < would be my favourite solution.
I would strongly suggest that you never store any kind of pointer in a container.
Instead, make a handle class which supports the required arithmetic and comparison operators.
It makes for code that's easier to reason about:
class objects {
public:
objects(double w, double h) : area(w * h) {}
bool operator<(const objects& r) const { return this->area < r.area; }
private:
double area;
};
struct object_handle
{
object_handle(shared_ptr<objects> const& ptr) : ptr_(ptr) {}
static object_handle create(double w, double h) { return make_shared<objects>(w,h); }
bool operator < (object_handle const& r) const {
return *ptr_ < *r.ptr_;
}
shared_ptr<objects> ptr_;
};
int main() {
std::vector<object_handle> mylist;
mylist.push_back(object_handle::create(10, 7));
mylist.push_back(object_handle::create(2, 5));
std::sort(mylist.begin(), mylist.end());
}
Lambdas are just syntactic sugar for a class with operator(), so you can very easily write one directly (especially if you don't need captures):
struct Comparator
{
bool operator() (const shared_ptr<objects> &lhs, const shared_ptr<objects> &rhs) const
{
return lhs->compareArea(*rhs);
}
};
myObjects.sort(Comparator());
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();
}
I would like to create a set containing the objects of my class, I have to determine a custom comparison. Unfortunately, everything I tried did not work.
class My_Class {
public:
char letter;
set<My_Class, compare> Children;
};
Ant then, the compare struct:
struct compare {
bool operator() (const My_Class& a, const My_Class& b) const{
return a.letter < b.letter;
}
};
How can I make this work please?
Currently, the issue displays that identifiers a and b are not declared.
You are trying to use compare structure inside My_Class, which uses My_Class in its method. It is not a trivial case, but forward declaration will help. So this should work:
class My_Class;
struct compare {
bool operator() (const My_Class &a, const My_Class &b) const;
};
class My_Class {
public:
char letter;
set<My_Class, compare> Children;
};
bool compare::operator() (const My_Class &a, const My_Class &b) const
{
return a.letter < b.letter;
}
Another alternative would be to pass comparator to std::set constructor, rather than specify it as a template parameter:
class My_Class {
public:
My_Class();
char letter;
set<My_Class> Children;
};
struct compare {
bool operator() (const My_Class& a, const My_Class& b) const{
return a.letter < b.letter;
}
};
My_Class::My_Class() : Children( compare() )
{
}
The problem with your code is that it is not guaranteed to compile. The problem is not the compare struct, so take that out of the picture. It is this:
class My_Class {
public:
char letter;
set<My_Class, compare> Children; // it is the set<My_Class> that is the problem
};
You are defining a std::set of My_Class before the definition of My_Class is known to the compiler. In other words, you're using an incomplete type within the std::set container. There is no guarantee that the code will compile, and even if it did, the behavior now is undefined.
If you want a container that works with incomplete types, you can use the Boost container types here:
http://www.boost.org/doc/libs/1_55_0/doc/html/container.html
Here is the description about Incomplete Types within the Boost documentation:
http://www.boost.org/doc/libs/1_55_0/doc/html/container/main_features.html#container.main_features.containers_of_incomplete_types
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++.