C++ Logo

std-proposals

Advanced search

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

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Thu, 25 May 2023 12:20:02 +0200
czw., 25 maj 2023 o 12:11 Frederick Virchanza Gotham via Std-Proposals
<std-proposals_at_[hidden]> napisaƂ(a):
>
> 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.
>

No, standard should not allow this, reason is simple
you hide multiple indirections that some could be `nullptr` or empty:
```
std::optional< std::optional<std::string>* > p { nullptr };
if (p) p->size(); // 2x UB and even if check look correct
(***p).size(); //easy to see that you need 3 checks
```

> 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();
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2023-05-25 10:20:14