C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Polymorphic operator new and polymorphic values

From: Hans Åberg <haberg_1_at_[hidden]>
Date: Wed, 30 Apr 2025 18:12:06 +0200
> On 30 Apr 2025, at 18:03, Jonathan Wakely <cxx_at_[hidden]> wrote:
>
> On Wed, 30 Apr 2025 at 17:01, Hans Åberg via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>>
>>
>>> On 30 Apr 2025, at 17:55, Jason McKesson via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>>
>>> On Wed, Apr 30, 2025 at 11:47 AM Hans Åberg via Std-Proposals
>>> <std-proposals_at_[hidden]> wrote:
>>>>
>>>>
>>>>> On 30 Apr 2025, at 15:55, Ville Voutilainen <ville.voutilainen_at_[hidden]> wrote:
>>>>>
>>>>> On Wed, 30 Apr 2025 at 16:33, Hans Åberg via Std-Proposals
>>>>> <std-proposals_at_[hidden]> wrote:
>>>>>>> I don't even know what polymorphic operator new means, so this doesn't
>>>>>>> seem like a very common scenario. Who would benefit from it being in
>>>>>>> the standard?
>>>>>>
>>>>>> The polymorphic operator new is also called a clone operator or a virtual copy operator, given such names rather than “new”. As I also use it for move, I call them polymorphic operator new. As “new” is a reserved word, it cannot be used without additions to the language.
>>>>>>
>>>>>>>> As for the val<A> type, I think the GC references are generally used just as a hack for polymorphic values:
>>>>>>>>
>>>>>>>> One can use reference counting, like in std::shared_ptr, or say the Boehm GC. But then one will have to remember when to copy an object, and there one gets errors in the implementation.
>>>>>>>
>>>>>>> I'm still not sure what you're actually proposing for the standard.
>>>>>>
>>>>>> Suppose:
>>>>>> class A {
>>>>>> virtual A* new_p(void* p) const& { return new A(*this); }
>>>>>> };
>>>>>>
>>>>>> class B : A {
>>>>>> virtual B* new_p(void* p) const& { return new B(*this); }
>>>>>> };
>>>>>>
>>>>>> class C : A {
>>>>>> // No new_p
>>>>>> };
>>>>>>
>>>>>> Then:
>>>>>> A* bp = new B, cp = new C;
>>>>>> bp->new_p(); // Gets a copy of *bp
>>>>>> cp->new_p(); // Gets a copy of A(*cp)
>>>>>> In the last copy, the object gets truncated to A, not the wanted full C object.
>>>>>>
>>>>>> So one must add by hand new_p to every new class created that is derived from A.
>>>>>>
>>>>>> An addition might be that one can write
>>>>>> class A {
>>>>>> virtual A* new_p(void* p) const& = default;
>>>>>> virtual A* new_p(void* p) && = default;
>>>>>> };
>>>>>> with the implication that every derived class D of A, as well as A, gets
>>>>>> class D : A
>>>>>> virtual D* new_p(void* p) const& { return new D(*this); }
>>>>>> virtual D* new_p(void* p) && { return new D(std::move(*this)); }
>>>>>> };
>>>>>
>>>>> So, use https://eel.is/c++draft/polymorphic
>>>>
>>>> The primary issue is to get proper copy and move of derived classes. On top of that, one can think of different interfaces.
>>>>
>>>> There I keep the allocation and the val<A> type together. So I have
>>>> // Placeholder struct and value
>>>> struct make_t {};
>>>> constexpr make_t make{};
>>>>
>>>> val<A> av(make, …); // Applies new A(…)
>>>> rather than
>>>> A* ap = new A(…);
>>>> val<A> av(ap;
>>>>
>>>> I cannot ditch the “make” argument, as there will be conflicts with the class val<A> constructors.
>>>
>>> I don't see how this is relevant. The point is that `std::polymorphic`
>>> is able to do the things you *need* done, even if they're not done the
>>> specific way you *want* it to be done.
>>>
>>> Your problem is that you have an `A*`, and you want to copy it, but
>>> you want the copy to be of the proper derived class that is behind the
>>> `A*`. The copy constructor of `polymorpic<A>` is able to use the copy
>>> constructor of the derived class stored, even though it only has `A`
>>> in its name.
>>>
>>> You can say that `polymorphic` is more cumbersome than a bespoke
>>> solution, but it solves your problem in its entirety.
>>
>> Can you give an example of how it is supposed to work?
>
>
> https://github.com/jbcoe/value_types/?tab=readme-ov-file#use

The issue here is to get a proper copy and move when it holds an object of a derived class, and this example does not tell that.

Received on 2025-04-30 16:36:59