r/cpp 13d ago

C++26: std::polymorphic

https://www.sandordargo.com/blog/2026/08/19/cpp26-polymorphic
97 Upvotes

44 comments sorted by

View all comments

Show parent comments

2

u/_Noreturn 13d ago

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

10

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 13d 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.

5

u/_Noreturn 12d ago edited 12d ago

You could say: "Well, they can never be equal". Right, right, but what about my next fancy class Square?

It is up to the operator== of the Base class, it is virtual and compares the typeid or whatever way you have.

```cpp class ShapeBase { ... virtual operator==(const ShapeBase& that) const=0; };

template<class T> class Shape : ShapeBass { ... bool operator==(const ShapeBase& that) const override { return typeid(that) == typeid(T) && (const T&)that == (const T&)*this; } }; ```

Wouldn't the 8 byte version require anothee indirection

Not necessarily, you can allocate the object and a (custom) vtable in one allocation.

So like this? ```cpp template<class T> struct Table { CopyCtor* copy; Drot* dtor; T object; // has to be last };

template<class B> struct polymorphic

{ polymorphic() { table = new Table<B>(); } polymorphic(auto&& obj) { table = new Table<decltype(obj)>(); }

B& operator() { return (B&)((char)table + 16)); } // 16 is 2 function pointers void table; }; ```

2

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 12d ago

It is up to the operator== of the Base class, it is virtual and compares the typeid or whatever way you have.

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...

So like this?

Looks like a possible implementation.

3

u/_Noreturn 12d ago

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/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 12d 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)

1

u/_Noreturn 12d ago

It isn't the responsibility of the polymorphic to do that. it is the virtual operator== the user provides can easily make it so it works like that way

5

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 12d ago

it is the virtual operator== the user provides can easily make it so it works like that way

You can't in the general case - which is why it is not provided...

0

u/_Noreturn 12d ago

Ugh, couldn't the same be said for indirect? it's operator== isn't usable if the underlying value doesn't have one.

5

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 12d ago

indirect<T> always contains an object of type T (or is valueless_after_move, which is trivial to handle) and therefore has no ambiguity about how operator== is supposed to work.

Apart from that: operator== for indirect<T> „Mandates“ (== static_assert) that T has an operator== on use, it T doesn’t have one, indirect<T> doesn’t have one.

But the key part is the first one, there is no ambiguity.

1

u/_Noreturn 8d ago edited 8d ago

I don't agree with your take, but that's fine. imo it should just be defined to be

cpp bool operator==(polymorphic& a, polymorphic& b) { bool has = a.is_valueless(); if(has != b.is_valueless()) return false; returb has && *a == *b; }

→ More replies (0)