https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102609

--- Comment #11 from waffl3x <waffl3x at protonmail dot com> ---
(In reply to Jonathan Wakely from comment #9)
> If we're right about that, then I agree that a warning would be useful for
> classes that have no such implicit conversion from S to S*.
> 
> I think the warning would give a false positive in the case below, so a way
> to disable it locally would be needed (e.g. via a diagnostic pragma).
> 
> struct S {
>   int f(this S*); // warning: explicit object parameter of pointer type can
> only match if an implicit conversion to S* exists
> };
> 
> struct T : S {
>   operator T*();
> };
> 
> T t;
> int i = t.f(); // derived class has the required conversion

I've ran some ideas in my head, no matter what is the decided upon design I
fully agree that all the warnings must be implemented with flags so they can be
suppressed, each independent of the other. I explain the warning I would
propose below, and I will make sure each one is properly documented, especially
since one of them has a clear risk of false positives, as you demonstrated.

(In reply to Gašper Ažman from comment #10)
> S* is not that useful as a
> type of an explicit object parameter, implicit conversions to pointer to
> yourself are probably about as weird as overloading operator&().

The only compelling case I can think of for such a thing would be passing a
pointer type that isn't related by inheritance anyway. That is to say, I'm not
disagreeing with you, I just stopped to think about it and came up with this,
consider the following example.

struct my_function {
    using func_ptr = void(*)(int);
    func_ptr _f;

    void operator()(this func_ptr f, int a) {
        return f(a);
    }

    operator func_ptr() const {
        return _f;
    }
};

I'm mostly just excited to share what I finally thought of as a good example
for an explicit object parameter that is not of a deduced or base type, but
it's never the less a pointer type.
Granted, I think this type of use should also have some sort of warning
available as it is still weird.

I think a warning for declaring an explicit object parameter as a pointer to
base, derived, or deduced type should be in, since a reference is probably the
best choice in that case. Perhaps deduced type shouldn't be included in that
warning, as there are cases that are kind of compelling for having that, one
could trap implicit conversions to any pointer type that way on derived
classes.

To elaborate on what I said above, I still think a warning when using a type
that is not related to the class should be provided. Even given that my example
above presents a legitimate pattern, I think it's use is infrequent enough to
provide an optional warning for this. I question if there's any value in
suppressing the warning when there are legitimate conversions present, as it
would no longer inform the user that they might have done something by mistake.
Essentially, since it's not possible to warn that there are no viable
conversions without false positives occurring, I think the warning should be
present simply because you are doing something weird, not because you are
declaring a function that won't be selected by overload resolution.

Just to reiterate, I fully agree that both warnings should be behind a flag. I
think I would propose that the first (warning for pointer to base, derived, or
deduced type in an explicit object parameter) should be turned on by default,
while the second should be turned off by default.

struct S : B {
    int f0(this B*) {} // warning 1, on by default
    int f1(this S*) {} // warning 1, on by default 
    int f2(this auto*) {} // warning 1, on by default?, separate warning?

    int f3(this int) {} // warning 2, off by default(?)
};

On the other hand, warning 2 could be split into 2 warnings, where one would
warn for a "weird" explicit object parameter, while the other would warn for
uncallable functions due to a lack of implicit conversions.
warning 2: weird explicit object parameter
warning 3: uncallable function (possibly has false positives)

struct S {
    operator int() const {}

    int f0(this int) {} // emits warning 2
    int f1(this some_type) {} // emits warning 2 or 3
};

I imagine my proposed warnings 2 and 3 should be mutually exclusive, but I'm
not sure which should take precedence.

I suppose I've bikeshedded this a bit but I wanted to get my ideas up here
before I start working on things again today.


> Also, don't forget explicit object function bodies and their type behave a
> lot more like static functions than member functions.

Yes, that is one of the next thing's on my list to implement. At the moment
they are still member function pointer types.
My current plan is;
1. Pass reference to object when calling explicit object member functions.
2. Convert the function type of explicit object member functions to regular
function types.
3. Make sure calling explicit object member functions as member functions still
works.
4. finally, implement calling explicit object member functions through a
pointer to function. (Which should hopefully be free to implement.)



A little off topic, but my function pointer wrapper example above got me
thinking about things and I don't have a better place to share them.
The example only really feels good because the implicit conversion is already
fairly natural for the type. What about cases where an implicit conversion
doesn't make sense, but still wants to benefit from such a pattern? I have to
wonder if a solution would be allowing private conversion operators to be
selected when calling an explicit object member function.
I guess that doesn't work properly when calling it through a pointer to the
function though, so surely this would have more issues than it's worth.
I have some other bad ideas on how to facilitate this, but they are far too
cursed to share here, so I will stop stalling and get to work.

Reply via email to