C++ Explicit Object Member Function with Void Parameter – Comprehensive Guide

c++c++23explicit-object-parameterlanguage-lawyervoid

According to cppreference since C++23

For a non-static non-virtual member function not declared with cv-qualifier or ref-qualifier, its first parameter, if not being a function parameter pack, can be an explicit object parameter (denoted with the prefixed keyword this).

Can this parameter be of type void? Current compilers allow it, but diverge in calling of such member function:

struct A {
    void f(this void) {}
};

// ok everywhere
auto p = &A::f;

int main() {
    // ok in GCC and MSVC
    p();

    // ok in Clang
    A{}.f();
}

Clang rejects p(); with the

error: called object type 'void (A::*)()' is not a function or function pointer

And GCC does not like A{}.f(); because

note: candidate expects -1 arguments, 0 provided

As well as MSVC:

error C2660: 'A::f': function does not take 0 arguments

Online demo: https://gcc.godbolt.org/z/zMaqaTTav

Which compiler is correct here?

Best Answer

The way the standard is currently written I would tend to say that Clang is the one behaving correctly.

According to [dcl.fct]/3:

A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list.

The this keyword does not affect the type of the parameter which is still void and it remains unnamed. Since it isn't inside a template, it is trivially not dependent either.

The rest of [dcl.fct] assumes that this adjustment of the declaration has been made without mentioning it explicitly again, for example when defining the type of a function in [dcl.fct]/5.

Therefore I think [dcl.fct]/6 which specifies the effect of this in the parameter declaration to declare an explicit object parameter should also be considered after the void-adjustment.

Then, since the this void parameter declaration was removed from the list, the function is not an explicit object function, but an implicit object function without any parameters.

The function's type will be void() and &A::f then forms a member pointer of type void (*A::f)(). Calling the member pointer with function call syntax p() is not possible.

Calling the non-static member function with a member access expression A{}.f() is however possible. this in the function definition will point to the temporary object materialized from A{}. No call arguments are needed because f has no parameters.

However, I am not sure whether this is actually the desirable behavior. The special adjustment of [dcl.fct]/3 exists only for C backwards-compatibility. There is no need to extend it to this void which isn't valid in C anyway. Instead I would think it should be made ill-formed like all other uses of (cv-qualified) void parameters.

The behavior of GCC and MSVC doesn't make sense either way. Even if this void still caused the function to be an explicit object function and wasn't ill-formed, then, whether or not this void is considered an actual parameter, the rest of the standard's specifications wouldn't be consistent. Either this would be an explicit object function without an explicit object parameter which is however required to be initialized in calls. Or it would be a function with a void parameter that needs to be initialized, but void objects do not exist in the first place.