[fpc-pascal] class inheritance and type incompatibility

Sven Barth pascaldragon at googlemail.com
Sun Sep 29 11:05:11 CEST 2013


On 29.09.2013 04:47, Xiangrong Fang wrote:
> 2013/9/29 Sven Barth <pascaldragon at googlemail.com
> <mailto:pascaldragon at googlemail.com>>
>
>
>     If you want to override the virtual Clone method of TTree<LongInt>
>     in TIntTree you need to use the same result type + override or
>     otherwise you gain nothing, because the compiler will not consider
>     the method as overloaded and use the Clone method of TTree<LongInt>
>     inside TTree<LongInt>'s methods instead.
>
>
> ​Yes, you are right, for virtual method, you need have exactly same
> type... But this leads me to think how the generics are implemented?

The important things about generics:
- The generic type itself (in this case TTree<>) does *not* exist inside 
the code (bugs not withstanding...), only the textual representation of 
the generic exists as metadata inside the PPU (which you can not access 
from a program)
- When specializing a generic the compiler loads this metadata and 
reparses the textual representation whereby the generic type parameters 
are replaced by the types you specified, so in code a "specialize 
TTree<Integer>" looks like a "TTree<T>" which you manually have made 
non-generic and replaced each occurence of "T" with "Integer".

> For
> example:
>
> === snippet 1 ===
> type
>    TIntTree = class(specialize TTree<Integer>)
>    public
>      function Clone: TTree; override;
>    end;
> === end of snippet 1 ===
>
> This will leads to a compile error: Generics without specialization
> cannot be used as a type for a variable.

"TTree" is not valid anywhere except inside the declaration of the 
generic (and only in non-Delphi modes).

>
> If so, why in the code TTree can be used everywhere, for example:
>
> === snippet 2 ===
> function TTree.Level: Cardinal;
> var
>    n: TTree;  <-- here TTree is not specialized.
> begin
>    ... ...
> end;
> === end of snippet 2 ===
>
> In snippet 1, why the compiler don't treat the TTree same as its own
> type i.e. TIntTree? I imagine that TTree in snippet 1 is logically
> similar to TObject. I mean, a class's method is of course possible to
> return a value of its parent's type or any other class's instance?

In case of generics in non-Delphi modes the own class name without 
generic parameters (here "TTree") is a place holder for the own type of 
the generic. The compiler replaces any reference to this type with 
references to the generic you are declaring. If you now specialize a 
generic (e.g. "specialize TTree<LongInt>") all those references are now 
handled as references to "TTree<LongInt>".

>
>     Would you please show how exactly you changed the code? With all
>     those methods calling each other it's a bit hard to imagine in my
>     head what you changed. ;)
>
>
> ​With your TSelfClass solution in TTree, I mean either:
>
> === solution 1 ===
> type
>    TIntTree = class(specialize TTree<Integer>
>    public
>      function Clone: TIntTree;
>    end;
>    function TIntTree.Clone: TIntTree;
>    begin
>      Result := TIntTree(inherited Clone); //typecast in TIntTree
>    end;
> ​=== end of solution 1===
> or:
> === solution 2 ===
> type
>    TIntTree = class(specialize TTree<Integer>)
>    end;
> begin //main
>    it2 := TIntTree(it1.Clone);  //typecast in main program
> end.
> === end of solution 2 ===​
>> Are both solutions correct?

Yes, correct are both.

Regards,
Sven




More information about the fpc-pascal mailing list