warning ... is hidden in ...
Steven Schveighoffer
schveiguy at yahoo.com
Mon May 5 07:33:41 PDT 2008
"Derek Parnell" wrote
> On Sat, 03 May 2008 11:43:59 +0200, Frits van Bommel wrote:
>
>>>> "Derek Parnell" <derek at psych.ward> wrote in message
>>>> news:1pg84i0mr3ihw$.1ng68jjvixchm$.dlg at 40tude.net...
>>>>> Using dmd 2.013 I'm now getting some messages along the lines of ...
>>>>>
>>>>> warning: class Foo Object opCmp is hidden in Foo
>>>>>
>>>>> What does this actually mean and how can I remove the warning (without
>>>>> removing the -w switch)?
>
>
>> Presumably your code currently looks something like:
>> ---
>> class C {
>> int opCmp(C c) {
>> // implementation
>> }
>> }
>
> Yes it does.
>
>
>> ---
>> If you want to get rid of the warning, try one of these:
>> ---
>> class C {
>> /// Overrides Object.opCmp, throws if the argument is not a C.
>> override int opCmp(Object o) {
>> if (C c = cast(C) o)
>> return opCmp(c);
>> throw new Exception("Can't compare C to " ~ o.classinfo.name);
>> }
>> int opCmp(C c) {
>> // implementation
>> }
>> }
>> ---
>> or
>> ---
>> class C {
>> override int opCmp(Object o) {
>> if (C c = cast(C) o) {
>> // implementation
>> }
>> throw new Exception("Can't compare C to " ~ o.classinfo.name);
>> }
>> }
>> ---
>> The second one is essentially the inlined version of the first, but will
>> be less efficient when the a and b in "a < b" are statically known to be
>> instances of C.
>> And both versions assume the argument isn't null; if you want to handle
>> null input (not sure why you would) you'll need some more checking.
>
> OMG!!!
>
> Firstly, this actually works!
>
> Secondly, WTF!!! And how exactly is this making coding easier for people?
>
> It would seem that every bloody time anyone codes a method that just
> happens to be defined in Object, you need to go through this syntactially
> hurdle to do something that seems so commonly required. For example, I
> also
> coded 'toString' in my class, which meant that I have to now ALSO code
> this
> waste of space ...
>
> override string toString(){ return this.toString();}
>
> This seems to be madness to me. What is it that I just not getting?
>
> I want coding to be as easy as possible which means that I expect the
> compiler to do the tedious work for me. Am I expecting too much? I mean,
> if
> I code in MY class a method called "toString()" why does the compiler
> bother to assume that I'm somehow referring to the method in Object?
I don't think you understand what the compiler is saying.
Look at a more pointed example:
class A
{
int foo(int i) {...}
}
class B : A
{
int foo(char[] c) {...}
}
Due to the simplified lookup rules of D, only B's foo or A's foo can be
called, depending on the instance type. I.e. D will only look in the most
derived scope of the static type that it finds the symbol 'foo', and won't
look any deeper at the base classes. So if you have:
B b = new B;
b.foo(1);
This will give you a compile-time error because you are trying to call A's
version of foo, but the compiler will only look at B's version, because that
is the scope it found foo's overload.
This is different than Java's lookup rules, which will look at base class
versions.
This is the SAME as C++ rules (which I was surprised to find out myself).
The question then becomes, what should happen in this case?
A a = new B;
a.foo(1);
This will compile, because the compiler knows that A has a foo(int) method.
However, there is some debate as to whether this should be allowed.
Walter's belief is that this should throw a hidden function exception. The
philosophy there is that if B is defining foo at all, it means to override
ALL versions of foo from the base class. Therefore, B doesn't want you
calling A's version of foo. In practice I think I have never needed this
functionality, nor can I see any reason for this, but this is Walter's
belief, and that's all that matters at this point :)
So now the latest D2 has a new warning (I thought it was an error, but no
matter) that you are compiling code that might throw an exception. This is
the reason for the warning.
If you want the Java behavior, you tell the compiler this by saying, "in
addition to these versions of foo, look at the base class' versions too" by
using alias:
class B
{
alias A.foo foo;
int foo(char[] c){...}
}
Now, all the above usage code of A and B compiles and does not throw an
exception.
My personal opinion is that this should be implicit. I can't see a reason
to have the C++ lookup rules over the Java rules, but that's my opinion, and
it differs from Walter's.
The reason you are having such trouble is because you are trying to override
the base class' version with a version that has a different signature. This
is not called overriding, but overloading. You are adding a NEW function to
the overload set. What you need to do is always override with the EXACT
same parameters as the base class. Look at the signatures for opCmp and
toString to see if they match your code. Using the override keyword helps
by ensuring the base class didn't change its signature and you didn't know
about it. If you use the override keyword and you are actually overloading,
not overriding, then the compiler gives an error.
The final thing is that you can override a base class' version of a
function, but return a co-variant type. That is, a type that implicitly
casts to the base class' return type. For example, if a function in your
base class returns an A, you can return a B and still override the same
function. That's because returning a B can be implicitly cast to returning
an A. This is handy so you don't have to upcast all the time when doing
things like chaining.
I hope this helps your understanding.
-Steve
More information about the Digitalmars-d-learn
mailing list