On Saturday, 10 March 2018 at 23:00:07 UTC, Jonathan M Davis wrote:

The idea is that the type can provide its own version of the function that is better optimized for it - e.g. it could potentially provide a member function find that is more efficient for it than std.algorithm.searching.find. That's actually the only technical reason why UFCS is superior to the normal function call syntax.

I think this may have hit the nail on the spot. So basically my thinking is that if you're going to use UFCS inside a generic function where you can't know what kind of methods the type has, do not do it unless you want this particular behavior or if your intent is to use a type's known API.

issue in practice. That doesn't mean that it's never a problem, but from what I've seen, it's very rarely a problem, and it's easy to work around if you run into a particular case where it is a problem.

Ya, it's easy to work around but the caveat there is you need to realize it's happening first, and add that to that it's "rarely a problem" and well ... now it seems scary enough for this to mentioned somewhere I'd say.

The one case that I am aware of where best practice is to avoid UFCS is with put for output ranges, but that has nothing to with your concerns here. Rather, it has to do with the fact that std.range.primitives.put has a lot of overloads for handling various arguments (particularly when handling ranges of characters), and almost no one implements their output ranges with all of those overloads. So, if you use put with UFCS, you tend to run into problems if you do anything other than put a single element of the exact type at a time, whereas the free function handles more cases (even if they ultimately end up calling that member function with a single argument of the exact type). We probably shouldn't have had the free function and the member function share the same name.

Oh, can you share a short example here maybe? Not sure I followed completely

Is it basically:

// if r is output range

r.put(a, b) // don't do this?

put(r, a, b) // but do this?

(Cause compilation error)

Is there something I'm not seeing as to why UFCS is not part of the overload set, and is there a way other than not using UFCS to prevent the silent hijacking?

If it were part of the overload set, then you have problems calling member functions, particularly because there is no way to call a member function other than with UFCS, whereas you can call free functions without UFCS, and if you _really_ want to be sure that a very specific function is called, then you can even give its entire module path when calling it. When UFCS was introduced, it was decided that having member functions always win out would cause the fewest problems.

- Jonathan M Davis

Ah yes, ok this makes sense.

Follow up:

How about if it's not part of the overload set, but is looked up if the function does not exist in the overload set. What would the problems there be?

Basically I don't see a reason why we wouldn't want the following to work:

struct S { void f() {} }
void f(S s, int i) {}
S().f(3); // error


Reply via email to