Proposal: expanding current subscripting facilities to include runtime subscripting into homogenous lists. abstract: We cant index into homogenous lists using facilities like tuples because they expect a Constexpr index.To fix this, this proposal proposes a new construct to give compiler intent and context that normal branching constructs or patterns relying on std::visit don't. The compiler should have complete freedom to store any book keeping information (no space complexity constraints) that it wants as long as the result is higher latency for accessing the values. Problem: Say you have homogenous values that you want to index, you are forced to use branching statements, polymorphism, or std::visit, all of which obscure the intent and context needed for the compiler to optimize those subscripts. Branching is the fastest so far, but even branching is more obscure and leads to code that in the words of Bjarne Stroustrup is "random code". solution: A construct that would have the same semantics as an std::array(runtime indexing, and a template instantiation for every collection that it is instantiated for), but is homogenous. (I call it half open in the previous mails because that's what Stroustrup calls sequential containers). To index, you can use an integer or any type that converts to one implicitly or explicitly(just like any other half open container types). To implement this container, the language would have to provide either one of these facilities on a language level: 1.Indexing user-defined objects or providing a new construct like struct/class that can be indexes. 2. Providing homogenous lists (unlikely to be implemented). 3. Providing a specific kind of function which can have different return types based on the index passed: {Int_obj, double_obj, float_obj} return_indexed_value(any integer that ful fills the is integral concept index); //The compiler deduces the body itself. A cherry on the top would be to allow any class to be based for indexing(those classes must have a particular function for providing the integer), so that you can give compilers an insight to the fact that you are potentially doing multiple subscript operations in the same chain, so the compiler can optimize that chain. For example: List[list[1]] the compiler can figure out ways to make this as cache friendly as possible. (This feature would probably work the same way if you provide an operator that converts the type to int, but again, this feature would make the intent more clearer). End note: Languages like C++ are often faster (than assembly) for large code bases because it has programming constructs that express intent and context more explicitly. Compilers can not optimize everything possible to optimize without significant overhead. That is why inline functions exist and why variadic template expressions can be folded. in fact, at the end of this video, the speaker explains that very well: https://www.youtube.com/watch?v=LDPMpc-ENqY Programming Language exist to provide intent to automated tools to provide better code. This feature would do just that. The code and the discussion below is to back my points up: code to check weather I am right: #include #include #include #include #include #include std::array array_1={1,2,3}; struct A { int get() { return array_1[0]; } }; struct B { int get() { return array_1[1]; } }; struct C { int get() { return array_1[2]; } }; struct data_accessed_through_visit { static std::variant obj; inline int operator()(int) { return std::visit([](auto&& arg) { return arg.get(); }, obj); } }; std::variant data_accessed_through_visit::obj=C{}; int user_index = 0; struct data_ternary { inline int operator()(int index) { return (index == 0) ? array_1[0] : (index == 1) ? array_1[1] : (index == 1) ? array_1[2] : -1; } }; struct data_switched { inline int operator()(int index) { switch(index) { case 0: return array_1[0]; case 1: return array_1[1]; case 2: return array_1[2]; default: return -1; } } }; struct data_indexing { inline int operator()(int index) { return array_1[index]; } }; volatile int x = 0; constexpr uint64_t loop_count=10000; static void measure_switch() { data_switched obj; for (int i=0; i++ void call_func(func_t callable_obj, int arg){ const auto start = std::chrono::steady_clock::now(); constexpr int how_much_to_loop=1000; for(int i=0; i++(end - start).count()/how_much_to_loop; std::cout<> user_index)) return 1; // Set the variant state if (user_index == 0) data_accessed_through_visit::obj = A{}; else if (user_index == 1) data_accessed_through_visit::obj = B{}; else if (user_index == 2) data_accessed_through_visit::obj = C{}; std::cout << "Time (ns) for switch: "; call_func(measure_switch, user_index); std::cout << "Time (ns) for visit: "; call_func(measure_visit, user_index); std::cout << "Time (ns) for ternary: "; call_func(measure_ternary, user_index); std::cout << "Time (ns) for subscript: "; call_func(measure_indexing, user_index); return 0; } the bench marks consistently show that these syntax constructs do matter (the smaller the index range is, the more the compiler can flatten it and know how to branch), notice how ternary is outperforming them all even though its nesting, This means that adding new syntax with the sole purpose to give compilers as much information as possible is actually useful. Consider how templates and instantiation give the compiler extra insight. why? because templates are instantiated at the point of instantiation which can be delayed upto link time. these are the benchmarks: benchmarks for g++: Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 33 Time (ns) for visit: 278 Time (ns) for ternary: 19 Time (ns) for subscript: 34 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 33 Time (ns) for visit: 296 Time (ns) for ternary: 20 Time (ns) for subscript: 35 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 34 Time (ns) for visit: 271 Time (ns) for ternary: 17 Time (ns) for subscript: 33 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 34 Time (ns) for visit: 281 Time (ns) for ternary: 19 Time (ns) for subscript: 32 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 34 Time (ns) for visit: 282 Time (ns) for ternary: 20 Time (ns) for subscript: 34 visual studio 2026: Time (ns) for switch: 97 Time (ns) for visit: 534 Time (ns) for ternary: 41 Time (ns) for subscript: 61 Time (ns) for switch: 91 Time (ns) for visit: 469 Time (ns) for ternary: 46 Time (ns) for subscript: 71 g++(-o3): the speed when -o3 is set on g++ is : PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 6 Time (ns) for visit: 11 Time (ns) for ternary: 2 Time (ns) for subscript: 2 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 6 Time (ns) for visit: 10 Time (ns) for ternary: 2 Time (ns) for subscript: 2 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 6 Time (ns) for visit: 11 Time (ns) for ternary: 2 Time (ns) for subscript: 2 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 6 Time (ns) for visit: 11 Time (ns) for ternary: 2 Time (ns) for subscript: 2 PS C:\Users\drnoo\Downloads> .\a.exe Enter index (0 for A, 1 for B, 2 for C): 2 Time (ns) for switch: 6 Time (ns) for visit: 11 Time (ns) for ternary: 2 Time (ns) for subscript: 2 (please ignore the wierd "drnoo" part, it's just a username that my father chose) before looking at the new solution, see the discussion that resulted in it: On Fri, 3 Apr 2026, 12:43 am Breno Guimarães, <******(for his privacy)> wrote: Hi Muneem, Can you post how the code would look like with the feature you are proposing? You don't need to try to teach everyone about how compilers work. Some of the things you say are just incorrect, so this will drag on. You showed the benchmark showing different implementations have different times. Great. How would the new improved version look like? It doesn't need to compile. Thanks! I replied with: Hi! Thank you for your feedback ❤️❤️❤️. Sorry if some of the things that I said were incorrect, but my philosophical point was that compilers are layered models that need a language to consume intent and context so they product proper code, that's just how it should be, why we use compiled languages. On the question of how the syntax will look like: It would have the same semantics as an std::array(runtime indexing, and a template instantion for every collection that it is instantiated for), but is homegenous. (I called it half open in the previous mails because that's what Stroustrup calls sequential containers). To index, you can use an integer or any type that converts to one implicitly or explicitly(just like any other half open container types). To implement this container, the language would have to provide either one of these facilities on a language level: 1.Indexing user-defined objects or providing a new construct like struct/class that can be indexes. 2. Providing homogenous lists (unlikely to be implemented). 3. Providing a specific kind of function which can have different return types based on the index passed: {Int_obj, double_obj, float_obj} return_indexed_value(any integer that ful fills the is integral concept index); //The compiler deduces the body itself. A cherry on the top would be to allow any class to be based for indexing(those classes must have a particular function for providing the integer), so that you can give compilers an insight to the fact that you are potentially doing multiple subscript operations in the same chain, so the compiler can optimize that chain. For example: List[list[1]] the compiler can figure out ways to make this as cache friendly as possible. (This feature would probably work the same way if you provide an operator that converts the type to int, but again, this feature would make the intent more clearer). how the newer syntax would probably look like (when compared to std::visit): #include #include struct A { int get() { return 10; } }; struct B { double get() { return 1; } }; struct C { float get() { return 2.1; } }; struct data_accessed_through_visit { std::variant obj= C{}; inline void operator()(int) { std::visit([](auto&& arg) { std::cout<< arg.get(); }, obj); } }; int main(){ int b=1; int c=2; { 1, b, c} return_indexed_value(std::size_t index);//can and should be declarable on the stack for //any bookkeeping information that the compiler might want to store //the captured values are captured during const lvalue references, so passing 1 should work return_indexed_value(2); data_accessed_through_visit a; a(2); //personally, i think the best solution for the problem should look something like this: //{ 1, b, c} return_indexed_value(std::size_t index); //A last value can be added to express  what is to be returned if the index is out of range, that would be awesome because the compiler //can even optimize that check.    return 0; } A side by side comparision is : // Current C++:#include #include #include struct A { int get() { return 1; } }; struct B { std::string get() { return "love C++";} }; struct C { double get() { return 2.1; } }; struct data_accessed_through_visit {     static std::variant obj;         inline void operator()(int) {         std::visit([](auto&& arg) {             std::cout<< arg.get();         }, obj);     } };//proves that std visit is too verbose and  slow (too hard/impractical to optimize) for this taskwhat about switches, enjoy the look for yourself(while imagining the look if this were repeated multiple times over hundreds of case statements):struct data_switched {     inline void operator()(int index) {         switch(index) {             case 0: std::cout<<1;             case 1: std::cout<<"love C++";            case 2: std::cout<<2;             default: return -1;         }     } }; fix:struct data_indexed { { 1, "love C++", 2} return_indexed_value(std::size_t index);  inline void operator()(int index) {         std::cout<