C++ Logo

std-proposals

Advanced search

Re: Secondary matching of overloaded operator->

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Tue, 15 Jun 2021 12:35:24 -0400
On Tue, Jun 15, 2021 at 12:19 PM Steve Thomas via Std-Proposals <
std-proposals_at_[hidden]> wrote:

>
> At the moment, an overloaded operator->() eventually resolves to a pointer
> of arbitrary type, before calling some method or accessing a data member
> from the type of that pointer.
>
> class SmartPointer {
> public:
> Pointee* operator->();
> };
> SmartPointer s;
> s->f(); // Resolves to a Pointee* p, then calls Pointee::*f(p).
>

Important detail: If the return type of `SmartPointer::operator->()` is not
a native pointer type, then the compiler will generate a call to *that*
type's operator->(), and so on forever or until a native pointer type is
finally reached.

I would like to change this, so that instead of immediately
> calling Pointee::*f(p), we look for a method in the current class scope and
> if found, call that instead:
> class SmartPointer {
> public:
> Pointee* operator->();
>
> template<typename R, typename... Args>
> T operator->(Pointee* p, R (Pointee::*f)(Args...));
> };
> SmartPointer s;
> s->f(); // Resolves to s.operator-><Pointee>(s->operator(),
> &Pointee::f);
>

Minor problem #1: What's the purpose of calling the zero-argument
`operator->()` here? Shouldn't this example be more like
    struct SP {
        T *ptr_;
        template<class R, class... As>
        R operator->(R (T::*pm)(As...));
    };
?

Major problem #2: What if `Pointee::f` has multiple overloads?

Major major problem #3: Show me the implementation of `operator->` above.
    struct SP {
        T *ptr_;
        template<class R, class... As>
        R operator->(R (T::*pm)(As...)) {
            return (ptr_->*pm)(args...); // wait, where the heck did these
`args...` appear from? How did we get them?
        }
    };

Major problem #4: What if `Pointee::f` is a data member, or
pseudo-destructor, or any of the other kinds of things that are allowed to
appear on the right-hand side of an `->` operator?

Minor problem #5: Couldn't you achieve your stated goal more easily by just
making your optional's `operator->` return a proxy object?
Are you familiar with the "arrow proxy" idiom?
https://quuxplusone.github.io/blog/2019/02/06/arrow-proxy/
I think you need some kind of proxy no matter what. You certainly don't
want `x->y()` to behave subtly differently from `(*x).y()`. So any solution
you come up with must work uniformly for both `operator->` and `operator*`.

I suggest you read about arrow_proxy, then see if it completely solves your
problem, and if not, then come back with a worked example — a complete
program, with a unit test that "fails now, but would pass if my fantasy
feature existed." Show the code you'd write using your fantasy feature that
would make the test pass. Then, people can either show different ways to
make it pass within today's language, or perhaps they might say "oh yeah, I
see how that would be useful."

HTH,
–Arthur

Received on 2021-06-15 11:35:40