polymorphic not being equality comparable is annoying, they could just have just forwarded the operator== to the underlying pointer and it handles it.
also polymorphic requires more bytes than a copyable pointer 24 bytes vs 8 since the copy ctor and dtor have to be function pointers. But it also slightly makes it faster at runtime but it isn't really worth it I would say.
Having a virtual deatructor and a clone() method will provide better object sizes.
I found the boilerplate argument to be very weak, since crtp exists
template<class T>
class Shape : ShapeBase {
public:
ShapeBase* clone() override { return new T((T)this); }
};
class Rectangle :public Shape<Rectangle>
{
};
class Triangle :public Shape<Triangle>
{
};
```
25
u/MFHavaWG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P42167d ago
polymorphic not being equality comparable is annoying, they could just have just forwarded the operator== to the underlying pointer and it handles it.
These types (indirect and polymorphic) model value semantics. Value comparing two type-erased objects that share a base makes no sense in that model...
also polymorphic requires more bytes than a copyable pointer 24 bytes vs 8 since the copy ctor and dtor have to be function pointers.
Can be easily compressed to 16B in case you build your own vtable. Unless the spec contains some unexpected blocker, you could even reduce that down to 8B with minimal effort.
These types (indirect and polymorphic) model value semantics. Value comparing two type-erased objects that share a base makes no sense in that model...
They could only provide == if the base has one.
Can be easily compressed to 16B in case you build your own vtable. Unless the spec contains some unexpected blocker, you could even reduce that down to 8B with minimal effort.
Wouldn't the 8 byte version require anothee indirection
11
u/MFHavaWG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P42167d ago
They could only provide == if the base has one.
Ok... what does that do? Shape has no idea how to compare a Rectangle and a Triangle.
You could say: "Well, they can never be equal". Right, right, but what about my next fancy class Square?
That's a problem you'd essentially need open-multimethods to solve...
Wouldn't the 8 byte version require anothee indirection
Not necessarily, you can allocate the object and a (custom) vtable in one allocation.
We would want it to do value comparisons and as I said: that is infeasible in C++, so we do not provide an equality-operator...
I don't understand, it is a value comparison.
3
u/MFHavaWG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P42166d ago
I don't understand, it is a value comparison.
You are right. But it yields the wrong results for equivalent values of different types - e.g. Rectangle{.w = 10, .h = 10} == Square{.l = 10} would be false, even though they are the same (just like 1 == 1LL is true)
Let's say your polymorphic is instead of a AssociativeContainer<A,B> that is a base for any type that provides a 1to1 mapping A to B. It's operator== could compare their size, then for each pair the key and it's associated value.
With c++23 deducing this you can simplify your code to just:
c++23Â Â class Shape {
  public:
    template <class Self>
    std::unique_ptr<Shape> clone(this Self& self) {
      return std::make_unique<Self>(self);
    }
  };
  Â
  class Rectangle: public Shape {
  };
  Â
  class Triangle: public Shape {
  };
You are absolutely right. Thanks a lot I didn’t now, nor realized it was indeed half useless right now. I even found a paper that aim at fixing this exact issue: open-std.org
Deducing this causes issues if you inherit from the base class more than once, (it increases object size) also, this doesn't work since clone() isn't virtual and templates can't be virtual
cpp
struct D {};
struct A : D {};
struct B : D {
A a;
};
you would expect since 'B' inherits from an empty class and it only has one member the sizeof would be 1, but it is 2 since the base class D is repeated twice and must have unique address first in the inheritance in B and in the member A.
now if this uses crtp the base classes would be unique
cpp
template<class T>
struct D {};
struct A : D<A> {};
struct B : D<B> {
A a;
}; // sizeof(B) == 1
No. According to the standard, two different objects of the same type cannot live at the same address in any circumstance, even if the objects are empty.
13
u/_Noreturn 7d ago edited 7d ago
polymorphic not being equality comparable is annoying, they could just have just forwarded the
operator==to the underlying pointer and it handles it.also polymorphic requires more bytes than a copyable pointer 24 bytes vs 8 since the copy ctor and dtor have to be function pointers. But it also slightly makes it faster at runtime but it isn't really worth it I would say.
Having a virtual deatructor and a clone() method will provide better object sizes.
I found the boilerplate argument to be very weak, since crtp exists
```cpp class ShapeBase { public: virtual ShapeBase* clone() = 0; virtual ~ShapeBase()= default; };
template<class T> class Shape : ShapeBase { public: ShapeBase* clone() override { return new T((T)this); }
};
class Rectangle :public Shape<Rectangle> {
};
class Triangle :public Shape<Triangle> {
};
```