C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed

From: Simon Schröder <dr.simon.schroeder_at_[hidden]>
Date: Wed, 8 Apr 2026 08:36:24 +0200


On Apr 8, 2026, at 2:06 AM, Muneem via Std-Proposals <std-proposals_at_[hidden]> wrote:


Summary of my points (to keep it simple for those who didn't understand):

***Type Safety is the priority***: 
With std::variant, you can assign a float to an object that should only be an int. That’s not type-safe. My type_set(selector) fixes this—once the runtime index picks the type, it’s fixed. You can't just swap it later.

Others have already mentioned it: std::variant is (according to the definition of type safety in C++) perfectly type safe. std::variant is currently built in a way that allows to change the underlying type within the bounds of the C++ type system. (Even a union is type safe, if we want to be nitpicking, because if you use it wrong it is undefined behavior. And code with UB is not valid C++. Only the compiler cannot detect all UB; so the union is a weak point.)

I do get that we would need extensions (only according to the zero overhead principle) to make the selected type of std::variant fixed after initialization. We could write a really light wrapper that enforces this (and probably behaves nicely in many other ways as well). I do see some problems with std::variant (or the aforementioned wrapper) automatically decaying into T, T& or T&&. Especially with a wrapper I believe smart people could come up with a way to do proper code generation using reflection. You’d be surprised with what reflection can already do now! This would be comparable to how expression templates allow code generation in a way that the compiler could never do itself. And how compilers actually work right now, it would be a lot easier for reflection (in the near future) to rewrite a function according to a specific type and generate switches or templates or a mixture of them. Maybe some attributes could also help with rewriting through reflection.

You should note that C++ prefers library solutions over changes of the syntax. If there is any way this can be done (as performant as with a compiler intrinsic) as a library, it should be done this way. And I am quite certain that a library solution using reflection is possible. And if not, there are probably small adjustments to reflection that would help everybody (and thus we should pursue those additions to reflection instead).

Since you are so adamant about your T^ type, please explain what would happen with the following code:
int^ x = {1,2.5,”C++”}.select(2);
x = 2;

Obviously, the select returns a C string and not an integer. So what code would the compiler produce? Would it just throw out the line
x = 2;
Or would everything that comes after
int^ …
be removed? How is the programmer supposed to reason about this?


****Compiler Freedom = Speed***: 
I’m calling for an implementation-defined container. This lets the compiler decide the best way to move or copy data (registers, stack, etc.) based on the ABI. It’s faster because the compiler isn’t fighting a rigid library structure. The compiler has the same freedom on when it instantiates type_set(selector) into T^. Tuples aren't implementation defined, since they have to provide some gurrenties and modifying tuples to fix this breaks the zero cost abstraction principle.

Yes, tuples have some guarantees. But, you want to have specific guarantees for your new type as well. BTW, nothing is prohibiting compilers to use a built-in type for std::tuple or any other type from the library.

And as I have said before in this very email: reflection could do the heavy lifting of generating specific code. Usually, every compiler has its own library implementation which would still make it kind of implementation defined by the compiler. Even better: we could try different library implementations with the same compiler and see which is better!

BTW, the examples of JSON and XML for a heterogeneous list would require runtime heterogeneous lists. Last, you have specified you’d want compile time known heterogeneous lists.

***Hybrid approach:*** I want compile-time known types with runtime indexing. std::tuple is too stiff for runtime, and std::variant is too loose. My proposal uses the existing branching/template mechanisms the compiler already has, just more efficiently. A mix of both of them is still bad since return variants would still lead to type unsafe code by being able to assign a float to int, as described in point 1.


On Wed, 8 Apr 2026, 4:59 am Muneem, <itfllow123_at_[hidden]> wrote:

Before my response to Mr.sebistian and Mr. Simon:
Small correction in my last emai(the change is in point 3):
1. The compiler can use a union, which case you have that advantage of the feature "potential merging multiple branches" that I talked about in my previous email.
2. The compiler can cause a function call after each definition
3.The compiler can do one of those, the second the variable is used or when the element has changed, the compiler can also choose the best point for instantion in between the varianle.defintion and when one of the two things happen.
4.will never do one of those if the list is filled only with const qualified types ( don't know if it's a good idea yet because of user defined types.
5.Again, in usage it will decay into T^, which can turn into at T& or T&&, so you can copy or move from it.


****Small recap*****:
Basically the issue with tuples and this:
(The mr mr.sebistian thinks it should work for heterogeneous lists):
class Dispatch
{
public:
    Dispatch(tuple<A, B, C> t, int index);
    void op1();
    void op2();
    tuple<A, B, C>& _tupleref;
    int _index;
};
tuple<A, B, C> t = { a, b, c }; // initialize
Dispatch<A, B, C> d(t, i); // select index i
i.op2(); // call operations
***Issues***
1.How do you get a tuple elements out, how do you get a tuple elements std::plus any type out. One way is to use variants, but then the variant returned can be assigned any type, which is again type safe. If I get an object of type A from the tuple or after adding the element at index 0 with another B, then I should not be able to assign B to it. That's not how type safe c++ is supposed to be.
The same issue exists for a array of variants 
2.It takes a type T^, why is that an issue?
Well, I may not be comfortable adding certain types in a list, for example for a bunch of representing a http packet, I may not be willing to have cookies in this list. The same applies to many users of heterogenous lists. Normal tuples won't allow me to put them in tuples and expect a special "safe" overload be called for them. In my proposals case, the overload will be that if T^; it fixes the downsides of the flexibility that heterogeneous lists provide.


Response to Mr.Sebistian:
>Just quickly answering 1)

>Would you rather have the whole tuple copied everytime?

>To the target stack frame?
>In your proposed solution you can also copy a pointer to the role or the whole tuple (as long as the optimizer does not optimize over the function call).
 ****Answer****
1.No, it would leave it to the compiler, by having a implementation defined container for this reason. Sometimes copying a tuple is efficient sometimes not. Many times however you would want to move the tuple, and return it back.
2.In my proposal, since the container is implementation defined hence the compiler has a free hand in copying it however it wants and moving it however it wants. 
3. I don't know what you mean by copying a role , but I think you meant pointing to an element using a point and copying that point.if yes:
Then no you can't do that, you can't have pointers to type_set(selector) but can have points to T^, just like you can point to any other glvalue(T^) is a glvalue.



 

>It will be faster only for small tuples, which perhaps can be passed in registers.
>But it is a simple change for Dispatch to store the tile instead of a pointer to the tuple.
>If you want to use the select to make the tuple smaller for directly copying the tuple, then just store the selected element as a variant in the Dispatch object.
****ANSWER****
1. No, my solution would be faster for heterogeneous list of any type because the compiler has full say in weather to copy it or not, and how to copy it, since the container is implementation defined.
2. What's a tile into the tuple? Please elaborate the thing that you mean by tile.
3. Variants don't have fixed types, hence for heterogeneous lists, they aren't an option at all. Even if say a A.dispatch(runtime index,std::plus, float{1}) return a variant that can have int, float, and I assign that variant to an object X of the same variant type, then object X can hold both float, int, but In heterogenous lists, I don't want that, I want the type to be fixed. So a tuple of built in types that have fixed types at each index is impossible, without the new value type type_set(selector).
Where as in my solution, object: type_set(selector) X= selector(runtime_index)
Can only hold what runtime_index was fixed to hold. So my proposal makes code more type safe and easy for reason about.  
 

>With your proposal the compiler has to break down your constructs, too. It also has to pass something to functions (if ABI boundary and not optimized over function calls). If you can program the same with current C++ we can reason about it.
 >I try to distill, what is actually new in your proposal.
>That it also cannot be provided by one layer of C++, which would lead to the same assembly anyway, if it is equivalent to your proposed language extension.
>Perhaps we find one small new language feature and another thing which can be expressed with some syntactic sugar.
>A language feature can be interface like ad-hoc relationships between types with common member function signatures.
>Otherwise perhaps 1 or 2 hints for the optimizer
****ANSWER****
1.thats why I proposed new value types, so that the compiler can use the existing branching and template instantion mechanisms. The compiler won't have to do anything other than use the existing mechanisms more efficiently to index the heterogeneous list. It basically uses the existing features compilers have. Like it does not have to break down constructs, just implementation defined container to get the type_set(selector), and instnatiate it into every possible T^. Like again, we have a million branching techniques the compilers already have.
2. It's not just 2 or 3 optimizations:
1. It's better type safety :
Type_set(selector) X= selector(runtime index)
The element at runtime index was int then you CANT assign float to X. This is an issue that std::variants fails to address. You may want to modify std::variant but that would either need changing how unions work or produce new expression type that my proposal already does.
2. Extended metaprogramming capabilities by simply merging current branching and template mechanisms.
3. This can be provided by one layer of c++ because c++ already has templates and branching facilities.
 

 

You want to move code generation to the location of the final op call, you don't want to use pointers, but want to send only the needed information through function call or even ABI boundaries.
****ANSWER****
1. I want code generation when ever type_set(selector) is used, and the compiler can merge the branches generated if it wants. I don't want to use pointers because again, they don't provide copy/move without having to allocate a new object all together or have the risk of slicing.
2. It's up to the compiler on what information to send for each branch.


My response to Mr.Simon:
JSON and XML where the examples I have provided before. However, these only work if we have a runtime heterogeneous list. Until now (especially with your example from the Turing virtual machine) we were talking about heterogeneous lists known at compile time. The solutions to these problems differ a lot: at compile time we can just use std::tuple. However, if we want to modify a list at runtime we are back to std::vector<std::variant> (or something similar). With your most recent explanations we are closer to the latter. However, the most efficient solution in your case would work with a std::tuple rather than with a std::vector<std::variant>. If the types are fixed at compile time the compiler can optimize much better.

So, which one do you want? Compile time lists or runtime lists? (Runtime indices would be allowed in both cases.)
****ANSWER****
I want a compile time known implementation defined container of type type_set(selector for the reasons described in the "Small recap" recap section.


On Tue, 7 Apr 2026, 5:01 pm Simon Schröder via Std-Proposals, <std-proposals_at_[hidden]> wrote:


On Tue, Apr 7, 2026 at 10:58 AM Muneem via Std-Proposals <std-proposals_at_[hidden]> wrote:
You may disagree for the need of T^ but again considering that heterogeneous lists will mostly be ever used in networking protocols, JSONs, XMLs, you want as the programmer to have a freehand in providing (application) safety, when you let a user index your list with any index he wants. 

JSON and XML where the examples I have provided before. However, these only work if we have a runtime heterogeneous list. Until now (especially with your example from the Turing virtual machine) we were talking about heterogeneous lists known  at compile time. The solutions to these problems differ a lot: at compile time we can just use std::tuple. However, if we want to modify a list at runtime we are back to std::vector<std::variant> (or something similar). With your most recent explanations we are closer to the latter. However, the most efficient solution in your case would work with a std::tuple rather than with a std::vector<std::variant>. If the types are fixed at compile time the compiler can optimize much better.

So, which one do you want? Compile time lists or runtime lists? (Runtime indices would be allowed in both cases.)
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2026-04-08 06:36:38