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

--- Comment #12 from Gašper Ažman <gasper.azman at gmail dot com> ---
Replies to relevant snippets inline.

That was quite a write-up!


> 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;
>     }
> };


This case is already covered by surrogate functions:

struct S {
    using fptr = int(*)(int);
    operator fptr() const {
        return +[](int){return 42;};
    }
};

int main() {
    S x;
    x(3); // OK, returns 42
}


https://godbolt.org/z/48Efzdr1q

The conversion operator is a better match in this case, so the
operator() which requires an implicit conversion is not the one
called.

>
> 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.

Converting to a pointer-type is not weird; it's converting to pointer
to your own type that is. Any proxy object with a conversion operator
to its underlying will have that if the underlying happens to be a
pointer type. Such a proxy object might well forward methods directly
to the underlying object via a conversion instead of stamping out a
body for each cv-ref qualifier.

> Granted, I think this type of use should also have some sort of warning
> available as it is still weird.

I disagree; conversion to your own type I think is a reasonable
warning (unless you have a conversion operator in the same class)
because it can come with a fixit hint (replace with reference to S
instead). All other types are legit.

> 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.

I definitely agree that this warning is reasonable.

> 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.

Yeah, deduced is legit, no warning for that one unless we discover
active pathologies.

> 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.

Strong disagree, that'll break a lot of code I actually intend to
write. Not stamping out additional template instantiations /  better
ABI by having implicit conversions will, I expect, be quite the common
implementation trick (for instance, any member algorithm on a vector
that doesn't change size can be called on an equivalent span instead
and therefore be passed in registers). This can be an implementation
detail of vector, so that vector and array can share template
instantiations, for instance.

> 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.

I think my ruminations above give a good example of why such false
positives should definitely be expected in well-behaved code.

> 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.

Yes, but you can get a pointer to it and call it directly. Might be a
useful way to pass overload sets.

> 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.

I'm not sure the second one should even exist, as it's bound to be
even more annoying than -Wshadow.

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

I say no warning on f2. S could be a CRTP-sans-CRTP mixin that expect
that conversion to exist.

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

I expect f3 to be a very common thing.

>
> > 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.)

What about forbidding the use of `this` and no implicit lookup in
bodies? You basically have to implement body parsing like it's a
static. You've probably thought of this, just making sure you don't
throw away a bunch of effort on the odd chance you missed it.

> 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.

Like programmable surrogates? Perhaps https://wg21.link/p2826 might be
of interest.

> 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.

Intriguing thought.

> 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.

:magic wand emoji:

Reply via email to