On Mon, Aug 2, 2021 at 8:34 PM Jason McKesson via Std-Discussion <std-discussion@lists.isocpp.org> wrote:
On Mon, Aug 2, 2021 at 7:41 PM Hani Deek via Std-Discussion
<std-discussion@lists.isocpp.org> wrote:
>
> The non-static member functions which we have now do not accept the 'implicit object argument' by value. It has to be passed by reference. If the proposal will simply introduce a new syntax to define that kind of functions, then the proposed syntax shouldn't allow the object parameter to be non-reference. If you do that, you should not run into any kind of problems with regard to the type of the function pointer.

OK, so let's look at this.

You want to discard literally every use case of this feature *except*
deduplication of `const` or other qualified parameters. You want a
feature focused specifically on just that. It's still going to have to
use templates to generate the various overloads, but you want it to
only work in that use case. So we give up recursive lambdas, better
CRTP, by-value members, SFINAE-friendly callables.

And in exchange, we get...

What?

Tell me. We get what? Fewer pages in the standard? In a standard that
already has eighteen *hundred* pages, we would have... *two* fewer
pages.

Because if what you care about is standard "complexity", it's *really*
just that. Go look at the current wording in P0847. It's incredibly
*short*. Most of its changes are turning "non-static member function"
into "implicit object function", a mere text replacement. There are a
couple of big blocks of text, but none of these are more than a single
page.

Also note that in this wording, explicit object functions are
considered non-static even though their function pointers are not
member pointers.

I'd like to second basically all of that.

Your proposal here is, basically, to add a restriction onto the object parameter of an explicit object member function such that it must be some kind of reference to the class type. But, why that restriction? We don't typically have restrictions on function parameter types. There's only one I can think of, and that's:

struct C {
    C(C); // can't do this
};

That's rejected because it makes zero sense to allow. That's a pretty reasonable restriction. Other restrictions we have are like... operator[] can only take a single parameter (possibly soon to be loosened) and not being able to have parameters after a parameter pack (proposal in flight, but this case does add complexity to overload resolution).

But in this context, adding a restriction to the explicit object parameter is a very unique kind of restriction in that we wouldn't be preventing any harm or avoiding complexity in other rules. Allowing a value parameter just falls out of the fact that it's a parameter. Restricting it to be a reference is complexity in its own right.

Additionally, in order to truly have member function type, you may have to reject cases like this:

struct B {
    template <typename Self> void f(this Self&&);
};
struct D : B { };
D().f();

Can the type of &B::f<D> really be a void(D::*)()? I don't actually know. It's certainly not obvious whether or not this would work.

The nice thing about the proposal as-is is that it's really actually quite small as far as language diff goes (as Jason pointed out), all the rules and behaviors just fall out of other rules and behaviors. The explicit object parameter is just a function parameter and has the same rules as other function parameters, and then explicit object member functions behave just like other functions. That's pretty nice.

The only possible benefit I see of restricting to reference types would be allowing virtual functions:

struct B {
    virtual void f(this B&); // ill-formed in the proposal
};

The paper goes into why virtual functions are problematic here, and this would alleviate those problems (by way of alleviating lots of other benefits), but that trade off doesn't seem worthwhile - since allowing you to write virtual functions this way doesn't let you do anything that you couldn't do before.

Barry