free func "front" is not picked up as a candidate when doing range.front(...)
aliak
something at something.com
Sun Feb 11 13:23:40 UTC 2018
On Thursday, 8 February 2018 at 22:57:04 UTC, Jonathan M Davis
wrote:
> D tends to be very picky about what it puts in overload sets in
> order to avoid function hijacking - e.g. it doesn't even
> include base class functions in an overload set once you've
> declared one in a derived class unless you explicitly add an
> alias to the base class function in the derived class.
>
> Also, D doesn't support "best match" with function overloads.
> The matches have to be exact - e.g. as soon as implicit
> conversions come into play, there can only be one match. If
> there's ever a conflict, you get a compilation error (unlike in
> C++, which tries to figure out what it thinks the best match is
> and go with that, sometimes with surprising results). And
> having both the member functions and free functions in the same
> overload set would basically require the compiler to go with
> the "best match," which simply isn't how D deals with
> overloads. Sometimes, that's annoying, but it also prevents a
> lot of subtle bugs that tend to crop up in C++ code.
>
> https://dlang.org/spec/function.html#function-overloading
> https://dlang.org/articles/hijack.html
>
> - Jonathan M Davis
Thanks for the articles, they shed a bit more understanding.
But I have some concerns/issues that maybe you or others can help
out with a bit more.
First, the link on function-overloading says "The function with
the best match is selected. " So I'm not sure what you mean about
subtle bugs in C++ and D doesn't support best match. If you have
foo(long), food(int) and call foo(3) in the same module, the best
match is selected in D and in C++. But, anyway, ignoring C++ for
now, after reading those articles you linked to my main issues
are D related, not C++, let me try to explain:
The rules of resolving overload sets seem to be to resolve the
issue where two overload sets have a matching function and one is
silently better. This is understandable and makes sense.
The case of a free function not being considered if there is a
member functions seems unrelated (as you say they are not part of
the overload set, but it seems like like maybe they should be).
In this case the member function does not match. If overload set
resolution was applied the free function would be considered and
be chosen as the only available candidate.
The rules for overriding, where functions with the same name in a
base class are hidden, also make sense. But not relevant here.
Infact, the hijacking article makes it seem to me that ufcs
hijacking has maybe been overlooked? Consider two modules which
are unrelated:
module a;
string f(R)(R r, int i) {
return "a.f";
}
string g(R)(R r) {
return "a.g";
}
---------
module b;
auto algo() {
static struct R {
void popFront() {}
enum empty = false;
int front() { return 1; }
}
return R();
}
----------
// Application programmer;
import std.stdio: writeln;
import a: f, g;
import b: algo;
void main(string[] args) {
auto r = algo();
auto a = r.f(3);
auto b = r.g;
writeln(a); // will print a.f
writeln(b); // will print a.g
}
All good so far. Until module b decides to add an internal
function to it's R type:
auto algo() {
static struct R {
// Add a function
string g() { return "internal g"; }
}
return R();
}
Application programmer updates module b...
auto b = r.g; // Does not do what programmer thinks anymore, and
no warnings.
Because a.g does not participate in overload set resolution
rules, the application programmer has an unknown bug (is it just
me or is this a bit... to put it lightly... scary?)
So that's the first problem. Then, secondly, say the people who
make module b like using proper access control levels, and mark
the internal string g() as private. Now there's a compiler error
in the application programmer, with no real clues as to why,
unless you deep dive in to D lang specs. And then you find out
you're getting a compiler error because of a private, internal,
function in a Voldermort type... ouch. Like. Big ouch. But ok,
this can at least be worked around:
import a: f, c = g;
Now:
auto b = r.c; // ok.
Third problem:
Module b person adds another internal function to their type.
auto algo() {
static struct R {
// Add a function
string f() { return "internal f"; }
}
return R();
}
Now application programmer has a compile error again. Again with
no clue. But this time it makes no sense because the line:
auto a = r.f(3);
Is clearly and unambiguously calling a.f(int). Again here if the
free function was part of the overload set resolution rules, it
would be resolved correctly.
Granted problem two and three are related. But there're subtle
semantic difference there. Either this has been overlooked or
there's a reason it is like this and must be like this that I've
yet to hear.
As it is right now (someone correct me if I'm wrong), for someone
who is writing libraries, it seems either impractical to use ufcs
because of compilation errors that it would cause in client code,
or, worse, unsafe because of silent bugs that would occur inside
client code AND library code in the case of types adding matching
function (i.e. same signature/name) in modules that the library
author and application author have no knowledge, or control of.
Cheers,
- Ali
More information about the Digitalmars-d-learn
mailing list