C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Extension to std::tuples to allow runtime indexing.

From: Simon Schröder <dr.simon.schroeder_at_[hidden]>
Date: Sat, 18 Apr 2026 17:28:49 +0200
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.




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

Received on 2026-04-18 15:29:07