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();
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