Virtual overloading of the comparison operator - c++

Suppose we have the following snippet:
class A
{
public:
virtual bool operator< (const A &rhs) const;
};
class B: public A;
class C: public A;
I want the comparison to depend on the real types of both the left and right hand side, for example:
x < y == true if type(x) == B and type(y) == C
x < y == false if type(x) == C and type(y) == B
The situation could be more complex, with much more derived classes than two. Of course, operator< has to be a virtual function. Is there an elegant way to write this?

///
/// goal: provide partial ordering of objects derived from A on the basis
/// only of class type.
/// constraint: ordering specified by us
///
#include <vector>
#include <typeindex>
#include <algorithm>
#include <iostream>
class A
{
public:
virtual bool operator< (const A &rhs) const = 0;
static const std::vector<std::type_index>& ordering();
};
template<class T> struct impl_A : public A
{
bool operator< (const A &rhs) const override
{
auto& o = ordering();
auto first = std::begin(o);
auto last = std::end(o);
auto il = std::find(first, last, typeid(T));
auto ir = std::find(first, last, typeid(rhs));
return il < ir;
}
};
class B: public impl_A<B> {};
class C: public impl_A<C> {};
const std::vector<std::type_index>& A::ordering()
{
// specify fording of types explicitly
static const std::vector<std::type_index> _ordering { typeid(B), typeid(C) };
return _ordering;
}
void test(const A& l, const A& r)
{
if (l < r) {
std::cout << typeid(l).name() << " is less than " << typeid(r).name() << std::endl;
}
else {
std::cout << typeid(l).name() << " is not less than " << typeid(r).name() << std::endl;
}
}
int main()
{
test(B(), C());
test(B(), B());
test(C(), B());
test(C(), C());
}
example output (clang):
1B is less than 1C
1B is not less than 1B
1C is not less than 1B
1C is not less than 1C
Fine! But (I was not precise enough in my question), when x and y share the same type (for example B), the result of x < y is given by a specific function const operator< (B &rhs) const in class ̀B. It is not necessarily false`.
OK, so we are revising requirements. This is a normal dialogue between users (who rarely realise the level of detail required in specifications) and the developers (who do!)
So this time we will say that any two dissimilar derived classes will have a consistent partial ordering (i.e. they will never compare equal and one will always compare less than the other) but we'll let the standard library decide which one comes first.
However, when the two classes being compared are of the same type, we would like to actually compare their values to determine ordering (and equivalence).
It would go something like this:
#include <vector>
#include <typeinfo>
#include <algorithm>
#include <iostream>
#include <tuple>
#include <iomanip>
class A
{
public:
virtual bool operator< (const A &rhs) const = 0;
std::ostream& print(std::ostream& os) const {
handle_print(os);
return os;
}
private:
virtual void handle_print(std::ostream&) const = 0;
};
std::ostream& operator<<(std::ostream& os, const A& a) {
return a.print(os);
}
template<class T> struct impl_A : public A
{
bool operator< (const A &rhs) const override
{
auto& rhs_info = typeid(rhs);
auto& lhs_info = typeid(T);
if (rhs_info == lhs_info) {
// same type, so do comparison
return static_cast<const T&>(*this).ordering_tuple() < static_cast<const T&>(rhs).ordering_tuple();
}
else {
return lhs_info.before(rhs_info);
}
}
};
class B: public impl_A<B> {
public:
B(int v) : _value(v) {}
auto ordering_tuple() const {
return std::tie(_value);
}
private:
void handle_print(std::ostream& os) const override {
os << _value;
}
int _value;
};
class C: public impl_A<C> {
public:
C(std::string v) : _value(std::move(v)) {}
auto ordering_tuple() const {
return std::tie(_value);
}
private:
void handle_print(std::ostream& os) const override {
os << std::quoted(_value);
}
std::string _value;
};
// now we need to write some compare functions
void test(const A& l, const A& r)
{
if (l < r) {
std::cout << l << " is less than " << r << std::endl;
}
else {
std::cout << l << " is not less than " << r << std::endl;
}
}
int main()
{
test(B(1), C("hello"));
test(B(0), B(1));
test(B(1), B(0));
test(B(0), B(0));
test(C("hello"), B(1));
test(C("goodbye"), C("hello"));
test(C("goodbye"), C("goodbye"));
test(C("hello"), C("goodbye"));
}
example results:
1 is less than "hello"
0 is less than 1
1 is not less than 0
0 is not less than 0
"hello" is not less than 1
"goodbye" is less than "hello"
"goodbye" is not less than "goodbye"
"hello" is not less than "goodbye"

The only solution I see is to not have the operator< function as a virtual member function, but as a set of overloaded non-member functions: One "default" function which takes two references to A as arguments, and then one overload each for the special cases.

Related

Printing all elements of multimap, which are pair of 2 different objects in C++?

I have 2 classes A and B. I create objects which are then put into a multimap. I want to print the all the keys with their corresponding values. However, my attempt to do this was not so successfull as I could create an iterator. I would like to ask how can I use the equal_range() method to accomplish this. Thanks in advance!
#include "pch.h"
#include <iostream>
#include <map>
using namespace std;
class A {
int key;
public:
A(int k) {
key = k;
}
A(A ob) {
key = ob.key;
}
int getKey() {
return key;
}
};
class B {
string value;
public:
B(string v) {
value = v;
}
};
multimap <A, B> mp;
int main() {
mp = {
make_pair(A(1),B("Crime and punishment")),
make_pair(A(1),B("The Idiot")),
make_pair(A(1),B("Brothers' Karamazov")),
make_pair(A(2),B("Andreshko")),
make_pair(A(2),B("The Gerak family")),
make_pair(A(3),B("The name of the rose")),
make_pair(A(3),B("Baudolino"))
};
for (auto ml = mp.begin(); ml != mp.end();ml++) {
multimap<pair<int, string>, pair<int, string>>::iterator it;
}
}
You already have your loop, but let's use C++11 ranged-loop instead.
You are just missing an accessor for the value, so let's assume that you have it (getValue) and just access the iterator:
for (const auto& pair : mp) {
std::cout << ml.first.getKey() << "\t" << ml.second.getValue() << std::endl;
}
Also change this:
A(A ob)
To
A(const A& ob)
This will give a real copy assignment. But the default copy constructor is also fine, so don't mention it at all, the default one is already good for you.
Some things you need:
Getter methods for class B.
A less than operator for your key class A.
A copy constructor for class A.
In the code below if remove code not needed (but would be nice to keep in live code) to show what is really used.
#include <iostream>
#include <map>
#include <string>
using namespace std;
class A {
int key;
public:
A(int k) : key(k){}
A(A const& ob) : key(ob.key) {}
A& operator=(A const& ob) = delete;
int getKey() const { return key; }
friend bool operator<(A const&left, A const&right) { return left.key < right.key; }
};
class B {
string value;
public:
B(string const& v) : value(v) {}
B(B const&) = default;
B& operator=(B const&) = delete;
string const& getValue() const { return value; }
};
multimap<A, B> mp;
int main() {
mp = {
make_pair(A(3), B("Baudolino")),
make_pair(A(1), B("Crime and punishment")),
make_pair(A(1), B("Brothers' Karamazov")),
make_pair(A(2), B("Andreshko")),
make_pair(A(1), B("The Idiot")),
make_pair(A(2), B("The Gerak family")),
make_pair(A(3), B("The name of the rose"))
};
for (auto const & item : mp) {
cout << "key:" << item.first.getKey() << " value:\"" << item.second.getValue() << "\"\n";
}
}

How can I define a parent class to reduce code duplication while using an abstract class with an equality operator definition?

I have below as 'simple' of an example of what I am trying to do as I could think up. I have an abstract class A which exposes a public interface to the world with two methods: operator== and performTasksSpecificToA. You can see that I'm using the 'Template Method Pattern' as well as the 'curiously recurring template pattern' in order to ensure that users of A don't need to worry about the implementation of A, in other words AImpl, while still being able to check equality against two instances of AImpl. See this answer on SO for a bit more information and context on this approach.
Now, suppose I wish to define a class B as follows:
class B
{
public:
virtual ~B() = 0;
bool operator(const B& b) const;
void performTasksSpecificToB();
};
As you can see, class B shares the same problem as A in terms of defining a public operator== for comparing sub-classes. How can I define a parent-class, let's call it Letter, in order to avoid duplicating code between A and B?
Here is my 'simple example', which compiles and runs.
#include <iostream>
class A
{
public:
virtual ~A() = 0;
bool operator==(const A& a) const;
void performTasksSpecificToA();
private:
virtual bool checkEquality_(const A& a) const = 0;
};
template <class T>
class A_ : public A
{
protected:
bool checkEquality_(const A& a) const override;
private:
virtual bool checkEquality(const T& t) const = 0;
};
class AImpl : public A_<AImpl>
{
public:
AImpl(int val) : val(val){};
bool checkEquality(const AImpl& anAImpl) const override;
private:
int val;
};
A::~A(){}
bool A::operator==(const A& a) const{
return checkEquality_(a);
}
template <class T>
bool A_<T>::checkEquality_(const A& a) const{
const T* other = dynamic_cast<const T*>(&a);
if (other != nullptr){
const T& me = static_cast<const T&>(*this);
return other->checkEquality(me);
}
return false;
}
bool AImpl::checkEquality(const AImpl& anAImpl) const{
return val == anAImpl.val;
}
int main(){
// factory:
AImpl* aImpl1 = new AImpl(1);
AImpl* aImpl2 = new AImpl(2);
AImpl* aImpl3 = new AImpl(1);
// client:
A& A1 = *aImpl1;
A& A2 = *aImpl2;
A& A3 = *aImpl3;
std::cout << "A1 == A2 -> ";
std::cout << (A1 == A2 ? "true" : "false");
std::cout << std::endl;
std::cout << "A1 == A3 -> ";
std::cout << (A1 == A3 ? "true" : "false");
std::cout << std::endl;
delete aImpl1;
delete aImpl2;
delete aImpl3;
return 0;
}
If you can allow Letter to be a template, you can simply have A inherit from a template base:
template<class T>
class Letter
{
public:
bool operator==(const Letter<T>& t) const {
const T& t1 = static_cast<const T&>(*this);
const T& t2 = static_cast<const T&>(t);
return t1.checkEquality_(t2);
}
private:
virtual bool checkEquality_(const T& a) const = 0;
};
class A : public Letter<A>
{
public:
virtual ~A() = 0;
void performTasksSpecificToA();
};
...
If you absolutely need a common Letter, you probably have to add another layer of CRTP like you did with A_ and A.

C++ - Map of Vectors of Smart Pointers - All inherited from the same base class

I've got this Map in my Entity-Component-System:
std::map<u_int32_t, std::vector<std::shared_ptr<Component>>> _componentMap;
The u_int32_t is the key to a vector of components. There can be multiple instances of the same component. (That's why there's a vector).
Now I would like to have a templated getter-function that returns a Vector of an inherited type:
template<class T> inline const std::vector<std::shared_ptr<T>> & getVector() const
{
u_int32_t key = getKey<T>();
return static_cast<std::vector<std::shared_ptr<T>>>(_componentMap.count(key) ? _componentMap.at(key) : _emptyComponentVec);
}
I know that this doesn't work, since std::vectors of different types are completely unrelated and I cannot cast between them. I would also like to avoid allocating a new vector every time this function is called.
But how I can I get the desired behaviour? When the the components are added I can create an std::vector of the desired derived type.
The question could also be: How can I have an std::map containing different types of std::vector?
For any solutions I can not link against boost, though if absolutely needed, I could integrate single headers of boost.
template<class It>
struct range_view {
It b, e;
It begin() const { return b; }
It end() const { return e; }
using reference = decltype(*std::declval<It const&>());
reference operator[](std::size_t n) const
{
return b[n];
}
bool empty() const { return begin()==end(); }
std::size_t size() const { return end()-begin(); }
reference front() const {
return *begin();
}
reference back() const {
return *std::prev(end());
}
template<class O>
range_view( O&& o ):
b(std::begin(o)), e(std::end(o))
{}
};
this is a quick range view. It can be improved.
Now all you need to do is write a pseudo-random-access iterator that converts its arguments. So it takes a random access iterator over a type T, then does some operation F to return a type U. It forwards all other operations.
The map then stores std::vector<std::shared_ptr<Base>>. The gettor returns a range_view< converting_iterator<spBase2spDerived> >.
Here is a crude implementation of a solution I have in mind for this problem. Of course, there are many rooms to refine the code, but hopefully it conveys my idea.
#include <iostream>
#include <map>
#include <vector>
#include <memory>
using namespace std;
class Base {
public:
virtual void f() const = 0;
};
class A : public Base {
public:
static const int type = 0;
explicit A(int a) : a_(a) {}
void f() const { cout << "calling A::f" << endl;}
int a_;
};
class B : public Base {
public:
static const int type = 1;
explicit B(int a) : a_(a) {}
void f() const { cout << "calling B::f" << endl;}
int a_;
};
class MapWrapper {
public:
template<class T>
void append(int a, vector<T> const& vec) {
types_[a] = T::type;
my_map_[a] = make_shared<vector<T>>(vec);
}
template<class T>
vector<T> const& get(int a) const {
return *static_pointer_cast<vector<T>>( my_map_.at(a) );
}
map<int, shared_ptr<void>> const& get_my_map() const {
return my_map_;
}
vector<shared_ptr<Base>> get_base(int a) const {
vector<shared_ptr<Base>> ret;
switch(types_.at(a)) {
case 0: {
auto const vec = get<A>(a);
for(auto v : vec)
ret.push_back(make_shared<A>(v));
break;
}
case 1: {
auto const vec = get<B>(a);
for(auto v : vec)
ret.push_back(make_shared<B>(v));
break;
}
}
return ret;
}
map<int, shared_ptr<void>> my_map_;
map<int, int> types_;
};
int main() {
MapWrapper map_wrapper;
map_wrapper.append(10, vector<A>{A(2), A(4)});
map_wrapper.append(20, vector<B>{B(5), B(7), B(9)});
for(auto const& w : map_wrapper.get_my_map())
for(auto v : map_wrapper.get_base(w.first))
v->f();
for(auto const& x: map_wrapper.get<A>(10))
cout << x.a_ << " ";
cout << endl;
for(auto const& x: map_wrapper.get<B>(20))
cout << x.a_ << " ";
return 0;
}
The solution was to use reinterpret_cast:
template<class T> inline std::vector<std::shared_ptr<T>> * getVector() const
{
auto key = getKey<T>();
return reinterpret_cast<std::vector<std::shared_ptr<T>> *>( (_componentMap.count(key) ? _componentMap.at(key).get() : const_cast<std::vector<std::shared_ptr<Component>> *>(&_emptyComponentSharedPtrVec)) );
}
It's not very pretty but it does work fine and it fulfills all requirements.

Global class comparison with inheritance

I am writing global class comparison functions with operator== for a large framework where classes tend to inherit several classes or have deep inheritance (class A inherits from B, B inherits from C, etc.). In order to make things manageable I figured I would have a comparison functions for base classes and then classes that inherit from a base would use that function in addition to checking their own members
Googling around, I found example code for comparing classes but no examples that involved inheritance. Below I made up a simple example for base class Foo which Bar inherits from:
#include <iostream>
class Foo
{
public:
int m_a;
Foo(int i) : m_a(i) {}
};
inline static bool operator==(const Foo& l, const Foo& r)
{
return l.m_a == r.m_a;
}
static void coutResult(const Foo& l, const Foo&r)
{
std::cout << "l.m_a == " << l.m_a << ", "
<< "r.m_a == " << r.m_a << ", "
<< (l == r ? "true" : "false") << std::endl;
}
class Bar :
public Foo
{
public:
int m_b;
Bar(int i, int j) : Foo(i), m_b(j) {}
};
inline static bool operator==(const Bar& l, const Bar& r)
{
return ((Foo)l) == ((Foo)r) &&
l.m_b == r.m_b;
}
static void coutResult(const Bar& l, const Bar& r)
{
std::cout << "l.m_a == " << l.m_a << ", "
<< "l.m_b == " << l.m_b << ", "
<< "r.m_a == " << r.m_a << ", "
<< "r.m_b == " << r.m_b << ", "
<< (l == r ? "true" : "false") << std::endl;
}
int main(int argc, char** argv) {
Foo a(1);
Foo b(1);
Foo c(2);
coutResult(a, b);
coutResult(a, c);
coutResult(a, c);
Bar d(1, 2);
Bar e(1, 2);
Bar f(1, 3);
Bar g(2, 2);
coutResult(d, e);
coutResult(d, f);
coutResult(d, g);
coutResult(e, f);
coutResult(f, g);
coutResult(f, g);
return 0;
}
It seems to work just fine but I was wondering if there was a "standard" way to go about this or a better solution. There are two problems I see with this solution:
Every time a developer adds a member to some class they will have to know to update the corresponding comparison function but I can't see how this could be avoided
No members can be made private and considering that the framework is large this is a problem. The only solution I know of is to make a getter for every private member
Your design has the potential to produce unexpected results.
If your main is:
int main(int argc, char** argv)
{
Foo a(1);
Bar d(1, 2);
coutResult(a, d);
return 0;
}
you'll end up comparing a Foo object with a Bar object and output will be:
l.m_a == 1, r.m_a == 1, true
If you are happy with that outcome, you can stick your current design. However, I think that is an inappropriate outcome.
My suggestions:
Make Foo a pure virtual class to avoid situations like that.
Make operator=() a pure virtual member function the Foo. Provide an implementation in Foo the derived class implementations can take advantage of.
Implement the function the derived classes. Use dynamic_cast to make sure that you are comparing a Bar with another Bar, not Bar with another sub-type of Foo.
Here's a program that demonstrates those ideas.
#include <iostream>
class Foo
{
public:
int m_a;
Foo(int i) : m_a(i) {}
virtual bool operator==(const Foo& r) const = 0;
};
bool Foo::operator==(const Foo& r) const
{
return (this->m_a == r.m_a);
}
static void coutResult(const Foo& l, const Foo&r)
{
std::cout << std::boolalpha << (l == r) << std::endl;
}
class Bar : public Foo
{
public:
int m_b;
Bar(int i, int j) : Foo(i), m_b(j) {}
virtual bool operator==(const Foo& r) const
{
Bar const* barPtr = dynamic_cast<Bar const*>(&r);
if ( barPtr == nullptr )
{
return false;
}
if ( !Foo::operator==(r) )
{
return false;
}
return (this->m_b == barPtr->m_b);
}
};
class Baz : public Foo
{
public:
double m_c;
Baz(int i, double c) : Foo(i), m_c(c) {}
virtual bool operator==(const Foo& r) const
{
Baz const* bazPtr = dynamic_cast<Baz const*>(&r);
if ( bazPtr == nullptr )
{
return false;
}
if ( !Foo::operator==(r) )
{
return false;
}
return (this->m_c == bazPtr->m_c);
}
};
int main(int argc, char** argv)
{
Bar bar1(1, 2);
Bar bar2(1, 2);
Bar bar3(2, 2);
Baz baz1(1, 10.8);
Baz baz2(1, 10.8);
coutResult(bar1, bar2);
coutResult(bar1, bar3);
coutResult(bar1, baz1);
coutResult(baz1, baz2);
return 0;
}
Output:
true
false
false
true

boost::static_visitor multivisitor non-variant arguments

Is there any inexpensive way to pass an arguments of non-variant types in addition to arguments of variant types, when multivisitor applyed?
What I mean by the term "expensive way" is:
#include <boost/variant.hpp>
#include <iostream>
#include <cstdlib>
struct A {};
struct B {};
enum class C { X, Y };
std::ostream &
operator << (std::ostream & out, C const c)
{
switch (c) {
case C::X : {
return out << "C::X";
}
case C::Y : {
return out << "C::Y";
}
default : {
break;
}
}
throw std::runtime_error("unknown C value");
}
using V = boost::variant< A, B >;
struct S
: boost::static_visitor<>
{
void
operator () (C const c, A const &) const
{
std::cout << c << " A" << std::endl;
}
void
operator () (C const c, B const &) const
{
std::cout << c << " B" << std::endl;
}
};
int main()
{
V const a = A{};
V const b = B{};
using VC = boost::variant< C >;
VC const x = C::X;
VC const y = C::Y;
S const s;
boost::apply_visitor(s, x, a);
boost::apply_visitor(s, y, a);
boost::apply_visitor(s, x, b);
boost::apply_visitor(s, y, b);
return EXIT_SUCCESS;
}
Another expensive way is to make non-static visitor with fileds of required types (or references to required types) and construct instances of such visitor for each set of values of non-variant types every time.