C++ Logo

std-proposals

Advanced search

[std-proposals] Chaining of the -> operator for std::optional< std::list<T>::iterator >

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 25 May 2023 11:11:30 +0100
Today I was re-working code that works with pointers to elements
inside an 'std::list'. Then at one point I wanted to erase an element
from the container, but to do so efficiently/properly, I needed an
iterator rather than a pointer.

So I refactored the code to use iterators instead of pointers. But of
course, you can't have a null iterator -- and I don't want to use
'container.end()' because the container can change in the meantime --
and so I decided to use an std::optional< std::list<T>::iterator >.

What I wanted to do is exemplified in the following code:

#include <optional>
#include <list>
#include <iostream>

struct Monkey {
    int i;
    Monkey(int const arg) : i(arg) {}
    void Speak(void) { std::cout << i << std::endl; }
};

std::list<Monkey> numbers;

std::optional< decltype(numbers)::iterator > AcquireNumber(int const val)
{
    if ( numbers.size() >= 16u ) return {};

    numbers.emplace_back(val);

    return --numbers.end();
}

int main(void)
{
    auto p = AcquireNumber(32);

    if ( p ) p->Speak();

    // The above line doesn't compile, I need to do:
    // if ( p ) p.value()->Speak();
}

Look at the second line in 'main'. I wanted to simply do "p->Speak()",
at first expecting that the operator->'s would be chained, but it
doesn't compile. Of course it doesn't compile because in that
particular syntax, you might want to access one of the members of the
iterator itself -- but most iterators don't have any accessible (i.e.
public or protected) members, so there would be no ambiguity if the
compiler allowed "p->" to go all the way to the element.

Can we make a change to the language so that this will become
possible? What I'm suggesting is as follows:
    When the chaining of operator-> has been completed, if the
resultant object doesn't have any accessible members, then instead of
applying '.' to the resultant object, apply '->' instead.

I was thinking of another possible proposal though. Suppose we could
mark the 'operator->' inside the iterator with the attribute
'extend_arrow_chain', for example:

template<typename T>
class iterator {
    T *p;
public:
    [extend_arrow_chain] T &operator->(void) { return *p; }
};

This attribute has the following effect:
        If the chaining of operator-> ever results in an object of
type "iterator", then instead of applying '.' to the object, apply
'->' instead.

This would allow us to do:
        std::optional< std::list<Monkey>::iterator > monkey;
        monkey->Speak();

But at the same time would still allow us to access members of the
iterator itself by using the '.' operator:
        (*(monkey.operator->()))->Speak();

Received on 2023-05-25 10:11:43