Date: Sat, 4 Apr 2026 11:43:35 +0200
> On Apr 4, 2026, at 6:13 AM, Andre Kostur via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Stop here. If I were to write:
>
> auto & x = return_index_chosen(read_from_string<uint8_t>(string_to_read_from,
> pos));
>
> What is the type of x ? Particularly if string_to_read_from is a runtime value.
I believe the picture is now getting clearer (Muneem, please correct me if I am misrepresenting your ideas). At compile time the type of x is not known. This is one of the reasons why Muneem suggested to use a JIT: just include the source code/AST/intermediate representation in the executable and finish compilation (using the JIT) when running the program. I see several problems with this: 1) C++ programmers love their performance. Waiting for JIT compilation is going to be slow. And every time we hit this place we might have to look up if the function has already been compiled for this type (which is a branch, of course, that we wanted to avoid). 2) If I want to call x.capacity() the member function needs to be linked in the executable. Linkers currently will only link the functions that are used by the program and eliminate all other functions. The reason is that we want to reduce the size of executables. 3) This is even exacerbated if we are using template types like std::vector: Its member functions are not available in any library that could be linked, but templates are instantiated when used. This means that we can’t even pre-generate all possible instantiations of std::vector::capacity (which through recursive use of std::vector are infinite) to be linked into the executable. At least with this approach it is highly questionable if the advantages of the proposal weigh heavier than the disadvantages I have mentioned. Most people most likely prefer smaller binaries and shorter compile time (less unnecessary template instantiations). It also is in contrast to the zero overhead principle: in order for this to work we would have to link everything every time even if we are not using this feature.
Now, I’m taking a turn to heterogenous lists: I didn’t know what these were and I’m not sure I understand them well enough to make a fully qualified judgement.
First, for the problems they are trying to solve: From what I can find they could be helpful when parsing JSON or XML or when returning results from SQL queries. However, if I’m not mistaken, all these formats have a known number of possible types. In general, in C++ we could use either std::variant or subtyping polymorphism to restrict the number of possible types in the heterogeneous list.
Muneem mentioned the programming language Go as an example. Go does not have subtyping. Instead, you can define interfaces. Traditionally, heterogeneous arrays have been defined as type []interface{} which is an array of an empty interface (disclaimer: I have never written a single line of Go). The empty interface somehow allows to store any type. The modern version of this seems to be to use an array of the ‘any’ type. This would perfectly map to std::vector<std::any> in C++. The example that I have seen is that it is still possible to at least println() every single entry inside an []interface{}. So, there needs to be some sort of runtime polymorphism. This again has the aforementioned problem of instantiating all templates and linking all functions.
Let’s think about how we could achieve runtime polymorphism for std::any (I don’t know how Go is actually doing it): First version that comes to mind would be to use runtime reflection and reflect on the string of the function name (e.g. “capacity”). This gets more complicated if function overloading is involved. (BTW, from what I have read about heterogeneous lists in C# it is convenient to use them, but slow!) Still, this does not achieve the goal of Muneem for this solution to be fast. We would need string comparisons and branching. I do believe that a decently fast solution would be possible as the Objective C runtime from Apple has shown (they use some clever tricks with caching, etc.). Still, it would add a performance overhead.
Muneem also mentioned something about indexing. What I could imagine is a function pointer for each entry in the heterogenous list. We could easily achieve something like this with std::vector<std::pair<std::any,std::function>> (or directly a function pointer instead of std::function). Then we could use the function pointer through the same index as the object. This gets more complicated if we want to have more than just a single function pointer. And we would need make it easier to create these kind of heterogeneous lists without explicitly mentioning which functions we want to have function pointers to. Also, if you have objects of the same type inside the heterogeneous list we might be able to reuse a set of function pointers and thus optimize storage. This sounds a lot like vtables. So, with this approach we have reinvented regular C++ runtime polymorphism.
As I have demonstrated before, std::any has problems with template instantiations and linking. An efficient solution (both in space and time) would need to know the exact types possible in the heterogeneous list. This kind of brings us back to std::variant. I would say that the current solution of std::visit over Muneem’s proposal is that I don’t have to figure out/know the index of the function I want to call on an object. std::visit is doing the job for me depending on the type. We could add some syntactic sugar and allow operator-> on std::variant to basically do the same job as std::visit with an automatically templated lambda (or even better with operator. if we get that one into the standard). The compiler might actually create a list of function pointers (even now with std::visit) that can be indexed based on the type. I don’t know if function pointers are better in any way for std::visit than the solution we have right now.
One thing I observe in Muneem’s examples is that the heterogeneous list is known at compile time. And the difference is that std::visit does polymorphism on function calls and what Muneem seems to envision is polymorphism on the object (type), i.e. when accessing an object from a heterogeneous list I don’t get a polymorphic object, but the object of its exact type. The biggest hurdle in this is how to get a runtime index into a compile time template parameter (preferably automatically): std::get<i>() on a std::tuple only works for compile time known values of i (some examples might work with the new ‘template for’ from C++26 reflection when iterating over all entries of the heterogeneous list if using a std::tuple). I’m currently stuck at this point (especially with Muneem’s actual problem with his Turing virtual machine).
Muneem, one last thing: I’m not sure if heterogenous lists are currently fast in any language. If you know of a truly efficient implementation worthy of C++ in any language, let us know. And with the way you are currently describing it I’m not sure if it works with a static type system. We would need to map runtime known indexes to compile time known types. This is practically impossible.
>
> Stop here. If I were to write:
>
> auto & x = return_index_chosen(read_from_string<uint8_t>(string_to_read_from,
> pos));
>
> What is the type of x ? Particularly if string_to_read_from is a runtime value.
I believe the picture is now getting clearer (Muneem, please correct me if I am misrepresenting your ideas). At compile time the type of x is not known. This is one of the reasons why Muneem suggested to use a JIT: just include the source code/AST/intermediate representation in the executable and finish compilation (using the JIT) when running the program. I see several problems with this: 1) C++ programmers love their performance. Waiting for JIT compilation is going to be slow. And every time we hit this place we might have to look up if the function has already been compiled for this type (which is a branch, of course, that we wanted to avoid). 2) If I want to call x.capacity() the member function needs to be linked in the executable. Linkers currently will only link the functions that are used by the program and eliminate all other functions. The reason is that we want to reduce the size of executables. 3) This is even exacerbated if we are using template types like std::vector: Its member functions are not available in any library that could be linked, but templates are instantiated when used. This means that we can’t even pre-generate all possible instantiations of std::vector::capacity (which through recursive use of std::vector are infinite) to be linked into the executable. At least with this approach it is highly questionable if the advantages of the proposal weigh heavier than the disadvantages I have mentioned. Most people most likely prefer smaller binaries and shorter compile time (less unnecessary template instantiations). It also is in contrast to the zero overhead principle: in order for this to work we would have to link everything every time even if we are not using this feature.
Now, I’m taking a turn to heterogenous lists: I didn’t know what these were and I’m not sure I understand them well enough to make a fully qualified judgement.
First, for the problems they are trying to solve: From what I can find they could be helpful when parsing JSON or XML or when returning results from SQL queries. However, if I’m not mistaken, all these formats have a known number of possible types. In general, in C++ we could use either std::variant or subtyping polymorphism to restrict the number of possible types in the heterogeneous list.
Muneem mentioned the programming language Go as an example. Go does not have subtyping. Instead, you can define interfaces. Traditionally, heterogeneous arrays have been defined as type []interface{} which is an array of an empty interface (disclaimer: I have never written a single line of Go). The empty interface somehow allows to store any type. The modern version of this seems to be to use an array of the ‘any’ type. This would perfectly map to std::vector<std::any> in C++. The example that I have seen is that it is still possible to at least println() every single entry inside an []interface{}. So, there needs to be some sort of runtime polymorphism. This again has the aforementioned problem of instantiating all templates and linking all functions.
Let’s think about how we could achieve runtime polymorphism for std::any (I don’t know how Go is actually doing it): First version that comes to mind would be to use runtime reflection and reflect on the string of the function name (e.g. “capacity”). This gets more complicated if function overloading is involved. (BTW, from what I have read about heterogeneous lists in C# it is convenient to use them, but slow!) Still, this does not achieve the goal of Muneem for this solution to be fast. We would need string comparisons and branching. I do believe that a decently fast solution would be possible as the Objective C runtime from Apple has shown (they use some clever tricks with caching, etc.). Still, it would add a performance overhead.
Muneem also mentioned something about indexing. What I could imagine is a function pointer for each entry in the heterogenous list. We could easily achieve something like this with std::vector<std::pair<std::any,std::function>> (or directly a function pointer instead of std::function). Then we could use the function pointer through the same index as the object. This gets more complicated if we want to have more than just a single function pointer. And we would need make it easier to create these kind of heterogeneous lists without explicitly mentioning which functions we want to have function pointers to. Also, if you have objects of the same type inside the heterogeneous list we might be able to reuse a set of function pointers and thus optimize storage. This sounds a lot like vtables. So, with this approach we have reinvented regular C++ runtime polymorphism.
As I have demonstrated before, std::any has problems with template instantiations and linking. An efficient solution (both in space and time) would need to know the exact types possible in the heterogeneous list. This kind of brings us back to std::variant. I would say that the current solution of std::visit over Muneem’s proposal is that I don’t have to figure out/know the index of the function I want to call on an object. std::visit is doing the job for me depending on the type. We could add some syntactic sugar and allow operator-> on std::variant to basically do the same job as std::visit with an automatically templated lambda (or even better with operator. if we get that one into the standard). The compiler might actually create a list of function pointers (even now with std::visit) that can be indexed based on the type. I don’t know if function pointers are better in any way for std::visit than the solution we have right now.
One thing I observe in Muneem’s examples is that the heterogeneous list is known at compile time. And the difference is that std::visit does polymorphism on function calls and what Muneem seems to envision is polymorphism on the object (type), i.e. when accessing an object from a heterogeneous list I don’t get a polymorphic object, but the object of its exact type. The biggest hurdle in this is how to get a runtime index into a compile time template parameter (preferably automatically): std::get<i>() on a std::tuple only works for compile time known values of i (some examples might work with the new ‘template for’ from C++26 reflection when iterating over all entries of the heterogeneous list if using a std::tuple). I’m currently stuck at this point (especially with Muneem’s actual problem with his Turing virtual machine).
Muneem, one last thing: I’m not sure if heterogenous lists are currently fast in any language. If you know of a truly efficient implementation worthy of C++ in any language, let us know. And with the way you are currently describing it I’m not sure if it works with a static type system. We would need to map runtime known indexes to compile time known types. This is practically impossible.
Received on 2026-04-04 09:43:50
