C++ Logo

std-proposals

Advanced search

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

From: Simon Schröder <dr.simon.schroeder_at_[hidden]>
Date: Fri, 10 Apr 2026 20:01:28 +0200
On 10. Apr 2026, at 17:39, Thiago Macieira via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> On Thursday, 9 April 2026 23:30:10 Pacific Daylight Time Muneem via Std-
> Proposals wrote:
>> template <std::size_t N, typename type_set, typename... Tail>
>> class heterogeneous_list_impl;
>>
>> template <std::size_t N, typename Head_t, typename... Tail>
>> class heterogeneous_list<N, Head_t, Tail...> : public
>> heterogeneous_list_impl<N - 1,Element_t<N, Head_t, Tail...>,Tail...> {
>> Head_t Element;
>> public:
>> using type_set = Element_t<N, Head_t, Tail...>;
>>
>> type_set operator[](std::size_t index) {
>> if (index == N) {
>> return type_set(Element);
>> } else if constexpr (N > 0) {
>> return this->heterogeneous_list_impl<N - 1, type_set,
>> Tail...>::operator[](index);
>> } else {
>> throw std::string{"out of bound access"};
>> }
>> }
>> };
>
> You do realise this is just std::variant with one extra operator, right?
>
> That operator[] could easily be implemented in the current std::variant, with
> a return either of a new type such as variant_element_ref or simply
> std::variant<std::add_reference_t<Types>...>. That return type is your
> innovation, not the heterogeneous list type (which is not a list).
>
Thiago, are you now mixing up std::variant and std::tuple? To me, it looks like heterogeneous_list is a std::tuple and Element_t is a std::variant. However, Element_t has a pointer to the proper element inside the heterogeneous_list in Muneems example implementation; so it would be comparable to a std::variant that stores a reference. (Which is partially what you said.)

I am a little bit confused by the example implementation: I fail to see how to actually store values inside the heterogeneous_list. Either there is only memory for the first element in the list or there is just a single list for each combination of <N,Head_t,Tail…>. I’m not really figuring this out by just looking at the code.

Basically, this is what I’ve been saying all along: the heterogeneous list can be a std::tuple with an additional operator[] and the return type is a special instantiation of std::variant which a) can store references instead of just values and b) once assigned cannot change its underlying type. b) is easy to achieve by using const std::variant because std::variant::operator= is a non-const member function. We might want to inherit from std::variant to create a new type with a new name that overloads operator= to be able to assign to the currently selected underlying type instead of changing the selection (I guess it would be really confusing to just add a const overload to std::variant::operator=, though technically it would work).

What I’m really missing from the proposed type is that it is always a reference to the heterogeneous list. In this way it is not possible for the user to choose between reference or copy. I think that this choice is really important. Back to a really simple example (I’m gonna use the STL types with features that don’t exist):
std::tuple<int,float> list{1,0.5f}
const std::variant<int,float> x = list[1]; // ‘1’ could also be a runtime value; make a copy of what is stored inside the list
const std::variant<int&,float&> y = list[0]; // get a reference to the object inside the list
const std::variant<const int&,const float&> z = list[1]; // now we have a reference, but cannot change the value inside the list
It would be helpful if std::variant<Ts&…> and std::variant<const Ts&…> would be convertible to std::variant<Ts…> (and std::variant<Ts…> to std::variant<const Ts…>).

Getting back to one of the previous examples:
int^ x = list[0];
I just noticed that this line could easily be written as
std::optional<int&> x = list[0];
This would allow us to write
*x = 2;
(if we are cocky and think we know that the optional has a value) or
x.value() = 2; // throws if x is nullopt

Sure, we could want nicer syntax than x.value(), but at least this simple example would work the way Muneem described it (or rather how I understood his description). (There was at least a proposal for std::optional<T&> at some point; but I’m not sure about its status.) If std::tuple::operator[] returns a const std::variant<Ts&…>, we might want to allow conversion from std::variant<Ts&…> to std::optional<T&> (where T is one of Ts…). The compiler can (or at least should) be able to see right through the optional if it knows it is not empty and optimize every access accordingly.

Here is how I would try to implement operator[] for std::tuple:
const std::variant<Ts&…> std::tuple<Ts&…>::operator[](std::size_t index) // also make the member function constexpr
{
    switch(index)
    {
    case 0:
        return Ts…[0];
        break;
    case 1:
        return Ts…[1];
        break;
    default:
        std::unreachable();
        break;
    }
}
We cannot write this by hand for every possible instantiation of the tuple, but I assume it is possible to use ’template for’ to fill the switch automatically based on the number of entries in the tuple. This would be just a couple of lines of code with reflection and no template metaprogramming (other than just the regular std::tuple stuff we already have).
Alternatively, we could write ‘return std::get<0>(*this);’ instead of ‘return Ts…[0];’ (I might have messed up the syntax because it is one of the newer features I haven’t used yet; probably needs to be a value and not a type).

This would make the proposal a short list:
1. Introduce std::variant<Ts&…>
2. Add operator[] to std::tuple which returns const std::variant<Ts&…>
3. Add conversion from std::variant<Ts&…> to std::optional<T&>
4. Implement lots of optimizations for std::variant and std::optional (and std::tuple::operator[] when used at compile time)

It can do (almost) anything requested. And the individual parts are not just for this one specific use case. Only the syntax does not look as nice.

Received on 2026-04-10 18:01:43