There is an Student abstract class, and two derived class Grad and Undergrad; and I want to overload operator in several ways.
student.h
class Student {
protected:
string Name;
int Stu_num;
public:
virtual void print() = 0;
bool operator==(const Student& x) const;
// constructor...
}
class Grad_Student : public Student {
private:
string Lab;
public:
void print();
bool operator==(const Grad_Student& x) const;
// constructor...
}
class Undergrad_Student : public Student {
private:
string Major;
public:
void print();
bool operator==(const Undergrad_Student& x) const;
// constructor...
}
student.cpp
bool Student::operator==(const Student& x) const {
if (this->Name == x.Name && this->Stu_num == x.Stu_num) {
if (typeid(*this).name() != typeid(x).name()) {
return false;
}
else if (!(strcmp(typeid(*this).name(), "12Grad_Student"))) {
return *dynamic_cast<const Grad_Student *>(this) == *dynamic_cast<const Grad_Student *>(&x);
}
else {
return *dynamic_cast<const Undergrad_Student *>(this) == *dynamic_cast<const Undergrad_Student *>(&x);
}
}
else {
return false;
}
}
bool Grad_Student::operator==(const Grad_Student& x) const {
return this->Lab == x.Lab;
}
bool Undergrad_Student::operator==(const Undergrad_Student& x) const {
return this->Major== x.Major;
}
To find student object in Student *students[300]
this operation == overloading works and doen't have problem,
but I want to implement overloading in different way using below.
How can I implement this function??
bool operator==(const Student& x, const Student& y)
{
// do comparison...
}
While that may appear to work, type_info::name() is
implementation-dependent,
not guaranteed to be unique between different types,
not guaranteed to be the same between different executions of the same program.
Comparing type_infos directly is reliable though, so you could do things like if (typeid(*this) == typeid(Grad_Student)) and it would work as expected.
However, polymorphism already exists, so you don't need to implement it yourself, and you can avoid a lot of trouble (and overhead) by dispatching to a virtual function instead of enumerating subclasses.
Something like this:
class Student {
public:
bool equals(const Student& s) const
{
return Name == s.Name
&& Stu_num == s.Stu_num
&& typeid(*this) == typeid(s)
&& equals_internal(s);
}
private:
virtual bool equals_internal(const Student& s) const = 0;
string Name;
int Stu_num;
};
bool operator==(const Student& lhs, const Student& rhs)
{
return lhs.equals(rhs);
}
class Grad_Student : public Student {
private:
string Lab;
bool equals_internal(const Student& s) const override
{
return Lab == static_cast<const Grad_Student&>(s).Lab;
}
};
class Undergrad_Student : public Student {
private:
string Major;
bool equals_internal(const Student& s) const override
{
return Major == static_cast<const Undergrad_Student&>(s).Major;
}
};
Note that the static_casts are safe, since Student has already established the type equality.
Related
I have this piece of code:
class ISerializable
{
public:
virtual bool operator==(const ISerializable* /*value*/) const { return false;};
virtual bool operator!=(const ISerializable* /*value*/) const { return true;};
};
class Point2I : public ISerializable
{
public:
bool operator==(const Point2I& value)
{
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value)
{
return !(*this == value);
}
public:
int x;
int y;
};
class Coordinate : public ISerializable
{
public:
virtual bool operator==(const Coordinate& value) const;
virtual bool operator!=(const Coordinate& value) const;
};
It is causing me -Woverloaded-virtual warning on gcc compiler.
I understand this warning due to that function declaration in Point2I hides virtual functions from ISerializable.
But I am not sure if just missing const in Point2I can cause this warning.
Can you please help me understand if it is const which is causing this warning or something else? Warning description from gcc didn't mention anything specifically.
Update:
I found another class Coordinate in my code base which was already overriding this and gcc not throwing warning for this. Only difference in Point2I and Coordinate is I didn't declare it virtual with const in Point2I. It appears just const is hiding base class declaration.
if it is const which is causing this warning or something else?
I'd say that it's something else, namely that you are not actually overriding the base class methods, even if you add const.
The argument const ISerializable* is not the same as const Point2I&.
One solution could be to override the base class methods, using const ISerializable& as the argument, and cast in the overridden methods:
class ISerializable {
public:
// add virtual destructor if you need to delete objects through
// base class pointers later:
virtual ~ISerializable() = default;
virtual bool operator==(const ISerializable&) const { return false; }
virtual bool operator!=(const ISerializable&) const { return true; }
};
class Point2I : public ISerializable {
public:
bool operator==(const ISerializable& value) const override {
auto rhs = dynamic_cast<const Point2I*>(&value);
// rhs will be nullptr if the cast above fails
return rhs && (x == rhs->x && y == rhs->y);
}
bool operator!=(const ISerializable& value) const override {
return !(*this == value);
}
private:
int x = 0;
int y = 0;
};
Example usage:
#include <iostream>
class Foo : public ISerializable { // another ISerializable
public:
};
int main() {
Point2I a, b;
std::cout << (a == b) << '\n'; // true - using Point2I::operator==
Foo f;
std::cout << (a == f) << '\n'; // false - using Point2I::operator==
std::cout << (f == a) << '\n'; // false - using ISerializable::operator==
// this makes the default implementation in ISerializable utterly confusing:
std::cout << (f == f) << '\n'; // false - using ISerializable::operator==
}
Another possible solution could be using CRTP but this would not work if you want to compare different types derived from ISerializable<T>:
template<class T>
class ISerializable {
public:
virtual ~ISerializable() = default;
virtual bool operator==(const T&) const = 0;
virtual bool operator!=(const T&) const = 0;
};
class Point2I : public ISerializable<Point2I> {
public:
bool operator==(const Point2I& value) const override {
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value) const override {
return !(*this == value);
}
public:
int x;
int y;
};
There are two problems.
The first one is different types of parameters
In these functions the parameters have the pointer type const ISerializable*
virtual bool operator==(const ISerializable* /*value*/) const { return false;};
virtual bool operator!=(const ISerializable* /*value*/) const { return true;};
and in these functions the parameters have the referenced type const Point2I&
bool operator==(const Point2I& value)
{
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value)
{
return !(*this == value);
}
The second one is that the first functions are constant member functions while the second functions are not constant member functions.
I have what is essentially class containing a std::map where the values are shared_ptrs wrapping a container which holds different types. Skeleton code follows:
// Just a basic example class
class MyClass {
public:
explicit MyClass(int i) : mI(i) {}
bool operator==(const MyClass& rhs) { return mI == rhs.mI; }
private:
int mI;
};
// A class into which key value pairs can be added where the value
// can be of a different type.
class MultipleTypeMap {
public:
template <typename T>
void AddObject(const std::string& key, const T object) {
auto ptr = make_shared<B<MyClass>>(std::move(object));
mSharedPtrMap.insert(pair<string, shared_ptr<A>>("key", ptr));
}
// ...
private:
class A {
public:
virtual ~A() = default;
};
template<typename T>
class B : public A {
public:
explicit B(const T& t) : item(t) {}
const T item;
};
map<string, shared_ptr<A>> mSharedPtrMap;
};
int main() {
MyClass m(1);
MultipleTypeMap multiMap;
multiMap.AddObject("test", m);
MyClass n(1);
MultipleTypeMap multiMap2;
multiMap2.AddObject("test", n);
if (multiMap == multiMap2) {
cout << "Equal" << endl;
}
return 0;
}
How should a generic == operator of MultipleTypeMap be written so that it compares the contents of mSharedPtrMap by checking that both the lhs and rhs objects have the same number of keys, the same keys and the same objects where same means that the == operator of the keys / objects evaluates to true?
If you type erase (and later on don't know which type you previously stored), then all the functionality must be provided by the base class interface. So, we need a virtual operator== in A that is implemented in each B.
Here is an implementation:
class MultipleTypeMap {
public:
template <typename T>
void AddObject(const std::string& key, T object) {
auto ptr = std::make_unique<B<T>>(std::move(object));
mMap.emplace(key, std::move(ptr));
}
// ...
bool operator==(const MultipleTypeMap& other) const
{
// Sizes must be equal.
if (mMap.size() != other.mMap.size())
return false;
// Sizes are equal, check keys and values in order.
auto itOther = other.mMap.begin();
for (auto it = mMap.begin(); it != mMap.end(); ++it, ++itOther)
{
if (it->first != itOther->first)
return false;
if (*it->second != *itOther->second)
return false;
}
// No differences found.
return true;
}
bool operator!=(const MultipleTypeMap& rhs) const { return !(*this == rhs); }
private:
class A {
public:
virtual ~A() = default;
virtual bool operator==(const A& other) const = 0;
bool operator!=(const A& other) const { return !(*this == other); }
};
template<typename T>
class B : public A
{
public:
explicit B(const T& t) : item(t) {}
bool operator==(const A& other) const override
{
const B<T>* otherB = dynamic_cast<const B<T>*>(&other);
// If the cast fails, types are different.
if (!otherB)
return false;
// Note: The above is probably slow, consider storing (on construction)
// and checking typeids instead.
// Check item equality.
return item == otherB->item;
}
const T item;
};
std::map<std::string, std::unique_ptr<A>> mMap;
};
Demo with tests
Note: I didn't fix every inconsistency in the original code. (Do you want to move or copy-construct your T? Why store const objects when your MyClass comparison operator is not const?)
I just build a mini program to understand how this will work because i need this for something a bit more difficult but i can't make this work.
I think i need to define operator overload but i dont know how because they are two objects of set<set<a>>
If you compile you will see a big error where it notice that he can't compare myset == myset2 and i think it will say same for operator != and =
#include <set>
using namespace std;
class a{
private:
int a_;
public:
int get_a() const{ return a_; }
void set_a(int aux){ a_=aux;}
bool operator < (const a& t) const{
return this->get_a() < t.get_a();
}
};
class b{
private:
set<set<a> > b_;
public:
void set_(set<a> aux){ b_.insert(aux); }
//Overload operators?
};
int main(){
b myset;
b myset2;
set<a> subset1;
set<a> subset2;
a myint;
myint.set_a(1);
subset1.insert(myint);
myint.set_a(2);
subset1.insert(myint);
myint.set_a(3);
subset1.insert(myint);
myint.set_a(5);
subset2.insert(myint);
myint.set_a(6);
subset2.insert(myint);
myint.set_a(7);
subset2.insert(myint);
myset.set_(subset1);
myset.set_(subset2);
myset2.set_(subset1);
myset2.set_(subset2);
if(myset == myset2){
cout << "They are equal" << endl;
}
if(myset != myset2){
cout << "They are different" << endl;
}
b myset3;
myset3 = myset2; //Copy one into other
}
In order for your code to work you need to specify following operators (note: they are not created by default)
class a{
private:
int a_;
public:
int get_a() const{ return a_; }
void set_a(int aux){ a_=aux;}
/* needed for set insertion */
bool operator < (const a& other) const {
return this->get_a() < other.get_a();
}
/* needed for set comparison */
bool operator == (const a& other) const {
return this->get_a() == other.get_a();
}
};
class b{
private:
set<set<a> > b_;
public:
void set_(set<a> aux){ b_.insert(aux); }
/* needed, because myset == myset2 is called later in the code */
bool operator == (const b& other) const {
return this->b_ == other.b_;
}
/* needed, because myset != myset2 is called later in the code */
bool operator != (const b& other) const {
return !(*this == other);
}
};
You should also take a look at http://en.cppreference.com/w/cpp/container/set and see what other operators std::set uses internally on its elements.
No operator (except for the default operator=(const T&) and operator=(T&&)) is generated by the compiler by default. You should define them explicitly:
class b{
private:
set<set<a> > b_;
public:
void set_(set<a> aux){ b_.insert(aux); }
//Overload operators?
bool operator==(const b& other) const {
return b_ == other.b_;
}
bool operator!=(const b& other) const {
return b_ != other.b_;
}
};
However, this only does not solve the case. Although comparison operators are already defined for std::set<T>, they only work if there are operators for T. So, in this case, you have to define operator== and operator!= for your a class in the same manner as I showed you with b class.
I'm writing my own typesafe enum header-only library at https://bitbucket.org/chopsii/typesafe-enums
The idea is to replace the non-type-safe c-style enum like:
enum ItemCategory
{
BLOCK,
WEAPON
};
with something that's properly type safe.
So far, my solution uses a macro that, for an example equivalent to the above enum, looks like this:
TypesafeEnum(ItemCategory,
(BLOCK)
(WEAPON)
);
And expands to something that looks like this:
template<typename InnerType>
class Wrapped {
public:
InnerType getValue() const { return _val; }
bool operator<(const Wrapped<InnerType>& rhs) const { ; return _val < rhs._val; }
bool operator>(const Wrapped<InnerType>& rhs) const { ; return _val > rhs._val; }
bool operator==(const Wrapped<InnerType>& rhs) const { ; return _val == rhs._val; }
private:
InnerType _val;
protected:
explicit Wrapped<InnerType>(const InnerType& val) : _val(val) {}
void setValue(const InnerType& val) { _val = val; }
};
class WrappedTypeItemCategory : private Wrapped<int>
{
private:
typedef const std::string* strptr;
typedef const std::string* const cstrptr;
explicit WrappedTypeItemCategory(const std::string& label, int val): Wrapped<int>(val), str(&label)
{}
cstrptr str;
public:
static WrappedTypeItemCategory make(const std::string& label, int val)
{
return WrappedTypeItemCategory(label, val);
}
void operator=(const WrappedTypeItemCategory& rhs)
{
;
setValue(rhs.getValue());
const_cast<strptr>(str) = rhs.str;
}
int getValue() const
{
return Wrapped<int>::getValue();
}
const std::string& getString() const
{
return *str;
}
bool operator<(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() < rhs.getValue();
}
bool operator>(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() > rhs.getValue();
}
bool operator==(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() == rhs.getValue();
}
friend std::ostream & operator<<(std::ostream &os, const WrappedTypeItemCategory& rhs)
{
;
return os << *rhs.str << "(" << rhs.getValue() << ")";
}
};
;
namespace {
template<typename T> class ItemCategoryInner : public TypesafeEnumBase
{
public:
static const WrappedTypeItemCategory BLOCK;
static const WrappedTypeItemCategory WEAPON;
static const std::string BLOCKStr;
static const std::string WEAPONStr;
};
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::BLOCK = WrappedTypeItemCategory::make(ItemCategoryInner<T>::BLOCKStr, 0);
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::WEAPON = WrappedTypeItemCategory::make(ItemCategoryInner<T>::WEAPONStr, 1);
template<typename T> const std::string ItemCategoryInner<T>::BLOCKStr("ItemCategory::BLOCK");
template<typename T> const std::string ItemCategoryInner<T>::WEAPONStr("ItemCategory::WEAPON");
struct ItemCategoryTemplateConstantTrick
{};
};
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
{
private:
const WrappedTypeItemCategory* const val;
public:
class InvalidValueError : public std::runtime_error
{
public:
const int val;
InvalidValueError(int val): std::runtime_error(std::string("Invalid value given for ") + "ItemCategory::make"), val(val)
{}
};
ItemCategory(const WrappedTypeItemCategory& value): val(&value)
{}
void operator=(const ItemCategory& rhs)
{
const_cast<const WrappedTypeItemCategory*>(val) = rhs.val;
}
static ItemCategory make(const int& val)
{
if (val == ItemCategory::BLOCK.getValue()) return ItemCategory(ItemCategory::BLOCK);
if (val == ItemCategory::WEAPON.getValue()) return ItemCategory(ItemCategory::WEAPON);
;
throw InvalidValueError(val);
}
const WrappedTypeItemCategory* const getWrappedValue() const
{
return val;
}
int getValue() const
{
return val->getValue();
}
const std::string & getString() const
{
return val->getString();
}
bool operator<(const ItemCategory& rhs) const
{
return *val < *rhs.val;
}
bool operator>(const ItemCategory& rhs) const
{
return *val > *rhs.val;
}
bool operator==(const WrappedTypeItemCategory& rhs) const
{
return *val == rhs;
}
bool operator!=(const WrappedTypeItemCategory& rhs) const
{
return !(*val == rhs);
}
bool operator<=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val < rhs);
}
bool operator>=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val > rhs);
}
void print(std::ostream& os) const override
{
os << *val;
}
friend std::ostream & operator<<(std::ostream &os, const ItemCategory& rhs)
{
rhs.print(os);
return os;
}
};
;
If I manually pre-expand it, like I have done here - by pre-compiling to file - then intellisense handles it all up until the line that says:
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
At which point it starts thinking ItemCategoryInner and ItemCategoryTemplateConstantTrick are ambiguous, along with many other things on every few lines of the file.
The header that contains this code is included in many places. I know I'm violating the One Definition Rule, which is why I'm using the Template Constant Trick, but I think I need to violate the ODR as my goal is to have an easy to use Macro based typesafe replacement for C++ enums.
I'm not sure if it's the violation of ODR that is the cause of my issues, or something else. I tried __declspec(selectany) but it didn't seem to help - and I would prefer if this macro would be eventually cross-platform, because if it works out, I have other projects I would use it in.
Either way, the .cpp files etc that make use of the enum are able to, and intellisense correctly suggests the options.
However, on a possibly related note, if I don't pre-expand the macro, intellisense isn't able to parse it and it doesn't know what a ItemCategory is at all, even though it compiles and works fine.
I just want my intellisense to work properly with my typesafe enums - it slows down intellisense and confuses it in other code in the same project.
I have a type hierarchy, and I'm not sure of a clean / good way to implement operator< and operator==.
Essentially, I already have this:
class Parent {
public:
virtual ~Parent() {}
};
class A : public Parent { int data; };
class B : public Parent { double data; };
class C : public Parent { std::string data; };
bool operator==(A const & lhs, A const & rhs) { return lhs.data == rhs.data; }
bool operator< (A const & lhs, A const & rhs) { return lhs.data < rhs.data; }
bool operator==(B const & lhs, B const & rhs) { return lhs.data == rhs.data; }
bool operator< (B const & lhs, B const & rhs) { return lhs.data < rhs.data; }
bool operator==(C const & lhs, C const & rhs) { return lhs.data == rhs.data; }
bool operator< (C const & lhs, C const & rhs) { return lhs.data < rhs.data; }
What I'd like to implement as well, is this:
bool operator==(Parent const & lhs, Parent const & rhs) { ... }
bool operator< (Parent const & lhs, Parent const & rhs) { ... }
I've currently implemented it by doing:
bool operator==(Parent const & lhs, Parent const & rhs) {
try {
return dynamic_cast<A const &>(lhs) == dynamic_cast<A const &>(rhs);
} catch(std::bad_cast const & e) {
}
try {
return dynamic_cast<B const &>(lhs) == dynamic_cast<B const &>(rhs);
} catch(std::bad_cast const & e) {
}
try {
return dynamic_cast<C const &>(lhs) == dynamic_cast<C const &>(rhs);
} catch(std::bad_cast const & e) {
}
assert(typeid(lhs) != typeid(rhs));
return false;
}
But this just seems awful. Is there a cleaner way of going about this?
For comparisons of complex types, you may find Double Dispatch useful.
If your types are very simple, it is sometimes effective to roll them all into one. In the example of 3 unsigned variants, it would likely be better to just use one type to accommodate all sizes, and to avoid dynamic dispatch and more complicated graphs of types.
Applied to original question; where A, B, and C all used unsigned types:
well, one quick and dirty approach would be:
class Parent {
protected:
virtual ~Parent() {}
public:
bool operator<(const Parent& pOther) const {
return this->as_uint64() < pOther.as_uint64();
}
// ...
private:
// using a type which accommodates all values
virtual uint64_t as_uint64() const = 0;
};
and then deriving from Parent would take the form:
class A : public Parent {
// ...
private:
virtual uint64_t as_uint64() const { return this->data; }
private:
uint16_t data;
};
then Parent could simply define all comparators, and all Parent types would be comparable.
Use a virtual comparator for single dispatch and dynamic_cast for type casting:
class ABC_base {
public:
virtual ~ABC_base() {}
bool operator < (ABC_base const & rhs) const {
return this->comparator(rhs) < 0;
}
protected:
virtual int comparator (ABC_base const &) = 0;
};
class ABC : public ABC_base {
protected:
virtual int comparator(ABC_base const & rhs) const {
try {
return my_comparator(dynamic_cast<ABC const&>(rhs));
// Run-time cast failed - use double dispatch as fallback
} catch (std::bad_cast&) {
return -rhs.comparator(*this);
}
}
private:
int my_comparator(ABC const & rhs) const {
if (data < rhs.data)
return -1;
if (data == rhs.data)
return 0;
if (data > rhs.data)
return 1;
}
T data;
};
Here's how the code works:
The base class's operator < is called, which uses dynamic lookup to find the comparator. It checks the returned value to see if it's lesser.
The derived class's comparator attempts to downcast the base class reference so that comparison can be done on the derived class's members.
Why the base class reference, instead of using the derived class reference?
Virtual dispatch would not work otherwise due to incorrect function signature.
Should the downcast succeed, it calls the non-virtual private comparator. Otherwise, it uses virtual dispatch again to do (rhs ? *this) and negates the result to compensate for the inverted ordering.
Why not have the cast and comparison in the one virtual function? It will make the code messier since the function will do two things: casting and comparing. Hence, there's a private comparator function. Should you want to use the base function in a derived class, along the lines of class ABC_der : public ABC, call ABC::comparator(static_cast<ABC const&>(rhs)). The use of Base:: forces static dispatch so you don't have to expose the helper comparison function.
Right now, this and rhs are of the same type, so we can finally do the actual comparison. A chain of if statements is used to return a value conformant to Java's Comparable and C's qsort() semantics.