Date: Sun, 19 Apr 2026 08:20:23 +0500
My main goal here is to simply provide better runtime indexing of tuples,
like my original goal was to extend branching but I believe that by
providing runtime indexed tuples, I can in fact do that, by simply
converting values used in branches into indexes.
On Sat, Apr 18, 2026 at 9:17 PM Sebastian Wittmeier via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Those 4 building blocks are a good direction.
>
>
>
> ===
>
>
>
> I still see as 5th topic a 'variant' class directly providing the common
> public interface of its possible types.
>
>
>
> That would replace the std::visit idiom with a direct usage.
>
>
>
> That can be done with the current std::variant in the following way:
>
>
>
> For each common public functions of the possible types it would create a
> static table of function pointers, one function pointer entry per type, one
> table per function signature.
>
>
>
> The type index in std::variant would point into the tables (as row index,
> same index for each table).
>
>
>
> All tables not used or all (if none is used) would be discarded by the
> linker.
>
>
>
> A lot of overhead for compiling and linking.
>
>
>
> Perhaps the tables would only be emitted, when the interface is actually
> called.
>
>
>
> Combining the interface for unrelated classes without specific declaration
> has precedence in how member functions of templated types are called T->f()
>
> They are seen as interchangeable just by their common name and signature.
>
>
>
> This would be the most radical change for Muneem's use case. And I thought
> the idea and discussion would bring more for it, as one of the original
> arguments was a replacement of std::visit.
>
>
>
>
> -----Ursprüngliche Nachricht-----
> *Von:* Simon Schröder via Std-Proposals <std-proposals_at_[hidden]>
> *Gesendet:* Sa 18.04.2026 17:29
> *Betreff:* Re: [std-proposals] Extension to std::tuples to allow runtime
> indexing.
> *An:* std-proposals_at_[hidden];
> *CC:* Simon Schröder <dr.simon.schroeder_at_[hidden]>;
> std-proposals_at_[hidden];
>
> I was a little busy and now I am a little late to the party.
>
> Most of my thoughts have already been stated by Sebastian. Also, Bjorn
> answered one of my questions (which I didn’t write, yet): std::optional<T&>
> already has a proposal (which I mentioned before). Thank you, Bjorn, for
> posting that number. Muneem, you should really read that proposal first.
> Chapter 5 (shallow vs deep const) shows how to discuss important design
> aspects. Remember the things I mentioned before that need a discussion?
> This is what I mean. (You might skip the “wording” chapter for now if you
> are inexperienced in writing proposals.)
>
> You are very strong on “guarantees” for your heterogeneous list. Usually,
> the standard does not give the kind of guarantees you imagine. There is
> nothing in a std::vector or std::string that gives any optimization
> guarantees. The additional semantic information for these types is
> completely invisible to the compiler and yet, the compiler is able to
> perfectly optimize these types. In the same way we don’t need any semantics
> for a heterogeneous list that the compiler can understand. Most
> importantly, every programmer should be able to write their own types that
> are as efficient as built-in types. This is one of the core principles of
> C++. A special type that needs to be handled differently by the compiler
> than any other existing type (and any other type we might add in the
> future) does not fit the principle of C++. As long as we stick to this
> principle (and you need a really strong reason if you want to change this
> core principle of C++ that has been there right from the beginning by
> Bjarne Stroustrup) the heterogeneous list can be implemented by a library
> without the compiler having any semantic understanding. Decades of
> optimization have shown that semantic understanding is not necessary (if
> even possible when considering stable ABIs across translation units; and I
> don’t mean backwards compatible ABIs in this specific case).
>
> We don’t need to break the ABI of std::tuple to make operator[] as fast as
> possible. Sebastian already hit the nail on the head. We all agree that a
> switch is one possible implementation. This can already be plenty fast if a
> CPU has branch prediction (which is every computer for decades). Sebastian
> already mentioned kind of a look up table that saves the offsets of the
> entries in the tuple. This still can be implemented without an ABI change.
> No extra bookkeeping is necessary inside the tuple. Even a library
> implementation is able to select between the two implementations based on
> the type of elements inside the tuple or the number of elements. Every
> decision a compiler can make can be made with a library implementation.
> With the look up table we have a single indirection which is about 2
> instructions. How much faster do you want to be?
>
> There is one optimization that can still be done. However, as in many
> cases, we only can get faster if we trade performance for memory. We don’t
> need a look up table if all elements inside the tuple have the same size.
> This is certainly achievable for std::tuple<int,int,int> (and this
> optimization can, again, be implemented inside a library). There is already
> a type that can achieve this: std::vector<std::variant> (or
> std::array<std::variant> or a plain C-style array of variants). Note that
> it would be a std::variant<Ts…> and not a std::variant<Ts&…>. You don’t
> want the variants to change their type? Make it a std::vector<const
> std::variant> instead. The best part about this solution? operator[] does
> not have to return a std::variant<Ts&…> because we can return a const
> std::variant<Ts…>& instead (which is what already happens right now). This
> is the perfect solution for the fastest implementation possible trading
> speed for additional memory overhead. No compiler can make the decision if
> you want the extra memory overhead to get even faster speeds than a
> std::tuple implementation could. You have to tell the compiler which one
> you want: is it std::tuple (with operator[]) or std::vector<const
> std::variant>? Both are heterogeneous lists. I think this is the perfect
> solution. I don’t know what extra optimization the compiler can do:
> directly indexing into a std::vector<const std::variant> (1 instruction) or
> std::tuple with a look up table for the offsets (2 instructions). You as a
> programmer can choose if 2 instructions are too many and you want to pay
> the price for the memory overhead. Much better than the compiler making
> that decision for you! (And for specific tuples the library can also switch
> to the 1 instruction solution automatically.) How is the compiler supposed
> to optimize better than 1 or 2 instructions? There is no additional
> bookkeeping information that you can provide the can generate faster code.
> And all this without breaking any ABI. We can do everything you want
> without introducing a new type for the heterogeneous list (we just need
> some additions).
>
> If you read the existing proposal for std::optional<T&> you’ll see that
> they suggest implementing it using a pointer internally. This is totally
> fine. Yes, pointers in general can be nullptr. However, here they are used
> to model a reference, so they are never null (especially this is not
> visible on the outside). The same is true for std::variant<Ts&…>. It is
> perfectly fine to use pointers internally. To the compiler both a reference
> and a pointer look the same. We don’t have std::optional<T&> and
> std::variant<Ts&…> yet only because there was disagreement and it was
> decided it was better to have optionals and variants without reference
> instead of not having them. The idea to allow references was there right
> from the beginning. It is not a totally new invention.
>
> Also, std::variant<Ts&…> does not need any additional bookkeeping
> information about where inside the std::tuple the value is stored. You just
> get a reference/pointer to the element. This type does not need anything
> special just because it is a return type of std::tuple::operator[]. It
> should just have the expected semantics that an extension of std::variant
> to allow references should have; entirely independent of this particular
> use case.
>
> The ingenuity of std::tuple::operator[] + std::variant<Ts&…> +
> std::optional<T&> + plus conversion from std::variant<Ts&…> to
> std::optional<T&> is its simplicity and that each of these four parts alone
> would be helpful in a lot of use cases. And the best part is that we don’t
> have to break ABI and still get perfect performance. Even without any
> special “guarantees”; the guarantee is that this is C++ and library
> implementers will look for the fastest solution possible. No additional
> compiler magic needed.
>
> And because these four parts stand on their own it makes a lot more sense
> to write four different proposals (well, the proposal for std::optional<T&>
> is already written) and have them reference each other.
>
> On Apr 18, 2026, at 4:24 PM, Muneem via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>
> 1.Unions of references or variants of references is a new concept, but
> references aren't, and because reference are literally aliases to an
> object, hence union/variants of reference should be too. To prove my self
> up, let's look at what a variant is conceptually.......: sum type, which is
> a kind of algebraic type, where as a reference type is a "value that
> enables a program to indirectly access a particular datum", sum types are
> sort of a UNION of types, which means that they have to preserve the core
> behaviour of their type. For some T, the core behaviour is to have a
> identity, its own invariant, and semantics. For some T*, it's the identy of
> the pointer and the semantics of the pointer. For a references, it's
> pointing to an object and that object only, So by using const &, you are
> just making the other object that is being pointed to as constant, not the
> reference itself, since the reference is comstant pointer to an object.
> More formally, you could a "A reference is a value that enables a program
> to indirectly access a particular datum".
> 2. What I meant, by "tuples have overhead" is that they have a fixed ABI
> for all their specaizations, which means that any book keeping has to be on
> the top of the tuple and can't be inside it. Since runtime indexed tuples
> are a completely different notion than compile indexed tuple since one is a
> product type like a struct. Runtime indexed tuples is heterogeneous list. A
> product type is not a product type if it has anything other than the
> objects of the multiple types it holds.
> Reference: https://en.wikipedia.org/wiki/Algebraic_data_type
>
>
>
>
> On Sat, 18 Apr 2026, 6:32 pm Thiago Macieira via Std-Proposals, <
> std-proposals_at_[hidden]> wrote:
>
> On Saturday, 18 April 2026 06:21:07 Pacific Daylight Time Muneem via Std-
> Proposals wrote:
> > 1. The "dedicated type" is variant<T&...> That I proposed. It is a
> problem
> > because a tuple elements is supposed have a single type (non reference
> > variants final).
>
> But you didn't explain why a variant of references must be final & const
> (cannot be reseated). You've said it has to, but not explained why.
>
> > 2. I am not repeating words, all I am saying is that runtime indexing a
> > heterogeneous list requires a new type. That type is
> runtime_indexed_tuple.
> > The reason, you can't use existing tuples is that they have a fixed ABI
> for
> > every specialization, hence you have to work with the overhead of that
> > tuple, while in my cass, the implementation can have its own completely
> new
> > ABI. No one should reinvent the wheel twice, especially blocks that
> aren't
> > optimized for that wheel, which in the case of Sebastian's example was
> the
> > variant of pointers and the tuple.
>
> Except that std::tuple has zero overhead: it has exactly the bits required
> to
> store the types in the type list, maybe some padding that can't be
> avoided,
> and no more. How can you make a type with less overhead?
>
> Did you mean one with more overhead that simplifies some other task by
> keeping
> other information? You need to explain why it's needed and what the
> trade-offs
> are.
>
>
> --
> Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
> Principal Engineer - Intel Data Center - Platform & Sys. Eng.
> --
> 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
>
> --
> 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
>
like my original goal was to extend branching but I believe that by
providing runtime indexed tuples, I can in fact do that, by simply
converting values used in branches into indexes.
On Sat, Apr 18, 2026 at 9:17 PM Sebastian Wittmeier via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Those 4 building blocks are a good direction.
>
>
>
> ===
>
>
>
> I still see as 5th topic a 'variant' class directly providing the common
> public interface of its possible types.
>
>
>
> That would replace the std::visit idiom with a direct usage.
>
>
>
> That can be done with the current std::variant in the following way:
>
>
>
> For each common public functions of the possible types it would create a
> static table of function pointers, one function pointer entry per type, one
> table per function signature.
>
>
>
> The type index in std::variant would point into the tables (as row index,
> same index for each table).
>
>
>
> All tables not used or all (if none is used) would be discarded by the
> linker.
>
>
>
> A lot of overhead for compiling and linking.
>
>
>
> Perhaps the tables would only be emitted, when the interface is actually
> called.
>
>
>
> Combining the interface for unrelated classes without specific declaration
> has precedence in how member functions of templated types are called T->f()
>
> They are seen as interchangeable just by their common name and signature.
>
>
>
> This would be the most radical change for Muneem's use case. And I thought
> the idea and discussion would bring more for it, as one of the original
> arguments was a replacement of std::visit.
>
>
>
>
> -----Ursprüngliche Nachricht-----
> *Von:* Simon Schröder via Std-Proposals <std-proposals_at_[hidden]>
> *Gesendet:* Sa 18.04.2026 17:29
> *Betreff:* Re: [std-proposals] Extension to std::tuples to allow runtime
> indexing.
> *An:* std-proposals_at_[hidden];
> *CC:* Simon Schröder <dr.simon.schroeder_at_[hidden]>;
> std-proposals_at_[hidden];
>
> I was a little busy and now I am a little late to the party.
>
> Most of my thoughts have already been stated by Sebastian. Also, Bjorn
> answered one of my questions (which I didn’t write, yet): std::optional<T&>
> already has a proposal (which I mentioned before). Thank you, Bjorn, for
> posting that number. Muneem, you should really read that proposal first.
> Chapter 5 (shallow vs deep const) shows how to discuss important design
> aspects. Remember the things I mentioned before that need a discussion?
> This is what I mean. (You might skip the “wording” chapter for now if you
> are inexperienced in writing proposals.)
>
> You are very strong on “guarantees” for your heterogeneous list. Usually,
> the standard does not give the kind of guarantees you imagine. There is
> nothing in a std::vector or std::string that gives any optimization
> guarantees. The additional semantic information for these types is
> completely invisible to the compiler and yet, the compiler is able to
> perfectly optimize these types. In the same way we don’t need any semantics
> for a heterogeneous list that the compiler can understand. Most
> importantly, every programmer should be able to write their own types that
> are as efficient as built-in types. This is one of the core principles of
> C++. A special type that needs to be handled differently by the compiler
> than any other existing type (and any other type we might add in the
> future) does not fit the principle of C++. As long as we stick to this
> principle (and you need a really strong reason if you want to change this
> core principle of C++ that has been there right from the beginning by
> Bjarne Stroustrup) the heterogeneous list can be implemented by a library
> without the compiler having any semantic understanding. Decades of
> optimization have shown that semantic understanding is not necessary (if
> even possible when considering stable ABIs across translation units; and I
> don’t mean backwards compatible ABIs in this specific case).
>
> We don’t need to break the ABI of std::tuple to make operator[] as fast as
> possible. Sebastian already hit the nail on the head. We all agree that a
> switch is one possible implementation. This can already be plenty fast if a
> CPU has branch prediction (which is every computer for decades). Sebastian
> already mentioned kind of a look up table that saves the offsets of the
> entries in the tuple. This still can be implemented without an ABI change.
> No extra bookkeeping is necessary inside the tuple. Even a library
> implementation is able to select between the two implementations based on
> the type of elements inside the tuple or the number of elements. Every
> decision a compiler can make can be made with a library implementation.
> With the look up table we have a single indirection which is about 2
> instructions. How much faster do you want to be?
>
> There is one optimization that can still be done. However, as in many
> cases, we only can get faster if we trade performance for memory. We don’t
> need a look up table if all elements inside the tuple have the same size.
> This is certainly achievable for std::tuple<int,int,int> (and this
> optimization can, again, be implemented inside a library). There is already
> a type that can achieve this: std::vector<std::variant> (or
> std::array<std::variant> or a plain C-style array of variants). Note that
> it would be a std::variant<Ts…> and not a std::variant<Ts&…>. You don’t
> want the variants to change their type? Make it a std::vector<const
> std::variant> instead. The best part about this solution? operator[] does
> not have to return a std::variant<Ts&…> because we can return a const
> std::variant<Ts…>& instead (which is what already happens right now). This
> is the perfect solution for the fastest implementation possible trading
> speed for additional memory overhead. No compiler can make the decision if
> you want the extra memory overhead to get even faster speeds than a
> std::tuple implementation could. You have to tell the compiler which one
> you want: is it std::tuple (with operator[]) or std::vector<const
> std::variant>? Both are heterogeneous lists. I think this is the perfect
> solution. I don’t know what extra optimization the compiler can do:
> directly indexing into a std::vector<const std::variant> (1 instruction) or
> std::tuple with a look up table for the offsets (2 instructions). You as a
> programmer can choose if 2 instructions are too many and you want to pay
> the price for the memory overhead. Much better than the compiler making
> that decision for you! (And for specific tuples the library can also switch
> to the 1 instruction solution automatically.) How is the compiler supposed
> to optimize better than 1 or 2 instructions? There is no additional
> bookkeeping information that you can provide the can generate faster code.
> And all this without breaking any ABI. We can do everything you want
> without introducing a new type for the heterogeneous list (we just need
> some additions).
>
> If you read the existing proposal for std::optional<T&> you’ll see that
> they suggest implementing it using a pointer internally. This is totally
> fine. Yes, pointers in general can be nullptr. However, here they are used
> to model a reference, so they are never null (especially this is not
> visible on the outside). The same is true for std::variant<Ts&…>. It is
> perfectly fine to use pointers internally. To the compiler both a reference
> and a pointer look the same. We don’t have std::optional<T&> and
> std::variant<Ts&…> yet only because there was disagreement and it was
> decided it was better to have optionals and variants without reference
> instead of not having them. The idea to allow references was there right
> from the beginning. It is not a totally new invention.
>
> Also, std::variant<Ts&…> does not need any additional bookkeeping
> information about where inside the std::tuple the value is stored. You just
> get a reference/pointer to the element. This type does not need anything
> special just because it is a return type of std::tuple::operator[]. It
> should just have the expected semantics that an extension of std::variant
> to allow references should have; entirely independent of this particular
> use case.
>
> The ingenuity of std::tuple::operator[] + std::variant<Ts&…> +
> std::optional<T&> + plus conversion from std::variant<Ts&…> to
> std::optional<T&> is its simplicity and that each of these four parts alone
> would be helpful in a lot of use cases. And the best part is that we don’t
> have to break ABI and still get perfect performance. Even without any
> special “guarantees”; the guarantee is that this is C++ and library
> implementers will look for the fastest solution possible. No additional
> compiler magic needed.
>
> And because these four parts stand on their own it makes a lot more sense
> to write four different proposals (well, the proposal for std::optional<T&>
> is already written) and have them reference each other.
>
> On Apr 18, 2026, at 4:24 PM, Muneem via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>
> 1.Unions of references or variants of references is a new concept, but
> references aren't, and because reference are literally aliases to an
> object, hence union/variants of reference should be too. To prove my self
> up, let's look at what a variant is conceptually.......: sum type, which is
> a kind of algebraic type, where as a reference type is a "value that
> enables a program to indirectly access a particular datum", sum types are
> sort of a UNION of types, which means that they have to preserve the core
> behaviour of their type. For some T, the core behaviour is to have a
> identity, its own invariant, and semantics. For some T*, it's the identy of
> the pointer and the semantics of the pointer. For a references, it's
> pointing to an object and that object only, So by using const &, you are
> just making the other object that is being pointed to as constant, not the
> reference itself, since the reference is comstant pointer to an object.
> More formally, you could a "A reference is a value that enables a program
> to indirectly access a particular datum".
> 2. What I meant, by "tuples have overhead" is that they have a fixed ABI
> for all their specaizations, which means that any book keeping has to be on
> the top of the tuple and can't be inside it. Since runtime indexed tuples
> are a completely different notion than compile indexed tuple since one is a
> product type like a struct. Runtime indexed tuples is heterogeneous list. A
> product type is not a product type if it has anything other than the
> objects of the multiple types it holds.
> Reference: https://en.wikipedia.org/wiki/Algebraic_data_type
>
>
>
>
> On Sat, 18 Apr 2026, 6:32 pm Thiago Macieira via Std-Proposals, <
> std-proposals_at_[hidden]> wrote:
>
> On Saturday, 18 April 2026 06:21:07 Pacific Daylight Time Muneem via Std-
> Proposals wrote:
> > 1. The "dedicated type" is variant<T&...> That I proposed. It is a
> problem
> > because a tuple elements is supposed have a single type (non reference
> > variants final).
>
> But you didn't explain why a variant of references must be final & const
> (cannot be reseated). You've said it has to, but not explained why.
>
> > 2. I am not repeating words, all I am saying is that runtime indexing a
> > heterogeneous list requires a new type. That type is
> runtime_indexed_tuple.
> > The reason, you can't use existing tuples is that they have a fixed ABI
> for
> > every specialization, hence you have to work with the overhead of that
> > tuple, while in my cass, the implementation can have its own completely
> new
> > ABI. No one should reinvent the wheel twice, especially blocks that
> aren't
> > optimized for that wheel, which in the case of Sebastian's example was
> the
> > variant of pointers and the tuple.
>
> Except that std::tuple has zero overhead: it has exactly the bits required
> to
> store the types in the type list, maybe some padding that can't be
> avoided,
> and no more. How can you make a type with less overhead?
>
> Did you mean one with more overhead that simplifies some other task by
> keeping
> other information? You need to explain why it's needed and what the
> trade-offs
> are.
>
>
> --
> Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
> Principal Engineer - Intel Data Center - Platform & Sys. Eng.
> --
> 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
>
> --
> 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-19 03:20:38
