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