Date: Wed, 30 Apr 2025 11:55:48 +0200
> On 30 Apr 2025, at 11:22, Jonathan Wakely <cxx_at_[hidden]> wrote:
>
> On Wed, 30 Apr 2025 at 10:06, Hans Åberg via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>>
>> I am using polymorphic (runtime, virtual) values val<A>; all classes A in the virtual hierarchy must have a polymorphic operator new. The polymorphic operator new is already present in C++, but one has to write it in by hand in each class; otherwise, the polymorphic copy and move operators will not work correctly.
>>
>> I primarily want a way to automate this, with an addition to the language, say by adding “default” in the base class and then also the derived classes get it, or alternatively, the template system. It would also be nice to be able to use the keyword “new” instead of something else like “new_p“.
>>
>> In my experience, the polymorphic values val<T> are better for objects that semantically are values than GC references, as the latter easily lead to programming errors. They also fit well with the C++ programming model, especially in view of move operators and C++17 copy elision.
>>
>> So polymorphic values could be added to C++, in their own right.
>>
>> The code for the class val<A> is in the file polymorphy.hh, and the operator new_p is defined in the file MLI.hh at lines 347–348:
>> https://git.savannah.gnu.org/cgit/metalogic-inference.git/tree/src
>
> Why should it be in the standard? It doesn't seem like something that
> many people will use.
The polymorphic operator new should be supported in derived classes somehow, as it is easy to forget when adding a new class, and then copy and move will be wrong.
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.
Also, there is no good way in C++ to “return *this” where the object *this is a GC reference, as one must know hos the object is allocated. I have used it with the type ref<A> in the file polymorphy.hh above, assuming that *this is allocated on the heap. In order to switch to std::shared_ptr, I had to remove all “return *this” occurrences. Before, I had tried the reference count the class hierarchy, and a custom operator new. The Boehm GC, I had to ditch because it puts locks around all allocations, so there was no time benefit in threading; one must have a lock-free GC for that.
There is no problem using “return *this” with the val<A> type. In fact, it fits very well with the C++ programming model.
So first, to get to use std::shared_ptr by removing “return *this”, I first was possible with val<A>, and then rewrote some remaining cases.
When using GC references, the idea is in expressions to substitute a variable just once in a Haskell lazy evaluation style, but I found it hard to do by hand. For example, in an expression like (x + a)*(x + b), one would represent x as a reference instead of two different occurrences, and just substitute that. But if there are simplifications that must be applied, the benefit is doubtful. So in the example (x + 2)*(x + b), substituting x with 1 would require 1 + 2 to be simplified, and there is no benefit in keeping a reference to x.
Anyway, those are my experiences, and the val<A> type could be added to C++ for such reasons, but I would be satisfied with just having an automated polymorphic operator new.
>
> On Wed, 30 Apr 2025 at 10:06, Hans Åberg via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>>
>> I am using polymorphic (runtime, virtual) values val<A>; all classes A in the virtual hierarchy must have a polymorphic operator new. The polymorphic operator new is already present in C++, but one has to write it in by hand in each class; otherwise, the polymorphic copy and move operators will not work correctly.
>>
>> I primarily want a way to automate this, with an addition to the language, say by adding “default” in the base class and then also the derived classes get it, or alternatively, the template system. It would also be nice to be able to use the keyword “new” instead of something else like “new_p“.
>>
>> In my experience, the polymorphic values val<T> are better for objects that semantically are values than GC references, as the latter easily lead to programming errors. They also fit well with the C++ programming model, especially in view of move operators and C++17 copy elision.
>>
>> So polymorphic values could be added to C++, in their own right.
>>
>> The code for the class val<A> is in the file polymorphy.hh, and the operator new_p is defined in the file MLI.hh at lines 347–348:
>> https://git.savannah.gnu.org/cgit/metalogic-inference.git/tree/src
>
> Why should it be in the standard? It doesn't seem like something that
> many people will use.
The polymorphic operator new should be supported in derived classes somehow, as it is easy to forget when adding a new class, and then copy and move will be wrong.
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.
Also, there is no good way in C++ to “return *this” where the object *this is a GC reference, as one must know hos the object is allocated. I have used it with the type ref<A> in the file polymorphy.hh above, assuming that *this is allocated on the heap. In order to switch to std::shared_ptr, I had to remove all “return *this” occurrences. Before, I had tried the reference count the class hierarchy, and a custom operator new. The Boehm GC, I had to ditch because it puts locks around all allocations, so there was no time benefit in threading; one must have a lock-free GC for that.
There is no problem using “return *this” with the val<A> type. In fact, it fits very well with the C++ programming model.
So first, to get to use std::shared_ptr by removing “return *this”, I first was possible with val<A>, and then rewrote some remaining cases.
When using GC references, the idea is in expressions to substitute a variable just once in a Haskell lazy evaluation style, but I found it hard to do by hand. For example, in an expression like (x + a)*(x + b), one would represent x as a reference instead of two different occurrences, and just substitute that. But if there are simplifications that must be applied, the benefit is doubtful. So in the example (x + 2)*(x + b), substituting x with 1 would require 1 + 2 to be simplified, and there is no benefit in keeping a reference to x.
Anyway, those are my experiences, and the val<A> type could be added to C++ for such reasons, but I would be satisfied with just having an automated polymorphic operator new.
Received on 2025-04-30 09:56:07