Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I can't pass 'Child' class instance when switching parameter type from 'const' to 'var' in 'overloaded' method

MCVE:

The following code does not compile with error when switching the parameter type from const to var or out in the overloaded method Train of the class TAnimalTrainer

but it compiles if non is specified.

[dcc32 Error] Project14.dpr(41): E2250 There is no overloaded version of 'Train' that can be called with these arguments

program Project14;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type    
  TAnimal = class
  private
  FName: string;   
  end;

  TDog = class(TAnimal)
  public
    constructor Create(Name: string);
  end;

  TAnimalTrainer = record // class or record
  public
    procedure Train({const}var aA: TAnimal); overload; // class method or not
    procedure Train(const aName: string); overload;
  end;

{ TAnimalTrainer }

procedure TAnimalTrainer.Train(const aName: string);
var
  Dog: TDog;
begin
  Dog := nil;
  try
    Dog := TDog.Create(aName);  
    Train(Dog); // error here
  finally
    Dog.Free;
  end;
end;

procedure TAnimalTrainer.Train(var aA: TAnimal);
begin
  aA := nil;
end;

{ TDog }

constructor TDog.Create(Name: string);
begin
  FName := Name;
end;



begin
  try       
    { TODO -oUser -cConsole Main : Insert code here }
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Found workarounds:

  • Omit the var.
  • Cast the local variable to TAnimal(Dog)
  • stick with const.

Question: Is this a bug in the compiler?

like image 562
Nasreddine Galfout Avatar asked Mar 22 '19 22:03

Nasreddine Galfout


1 Answers

Is this a bug in the compiler?

No it is not.

Although you have discovered this in the context of an overloaded method, the overloading is disguising the real problem. It will be much easier to understand the issue if we remove the overload.

So, to that end, consider this program:

type
  TAnimal = class
  end;

  TDog = class(TAnimal)
  end;

procedure GetAnimal(var AAnimal: TAnimal);
begin
  AAnimal := TAnimal.Create;
end;

var
  Dog: TDog;

begin
  GetAnimal(Dog);
end.

This fails to compile in the call to GetAnimal with this error:

[dcc32 Error]: E2033 Types of actual and formal var parameters must be identical

Why does the compiler reject this? Well, imagine if it accepted this. If it did so then when GetAnimal returned the Dog variable would refer to an object that was not a TDog.

To see this, change the body of the program to look like this:

GetAnimal(TAnimal(Dog));
Writeln(Dog.InheritsFrom(TDog));

When you do so, the program compiles, but the output is

FALSE

In the context of your program, the compiler is faced with some overloads. As we have seen in this example, the compiler cannot accept passing a TDog variable to a TAnimal var parameter, so it rejects that overload. It knows that it cannot pass a TDog variable to a string parameter, so that is rejected. At which point, there are no overloaded methods left, hence the error message.

like image 133
David Heffernan Avatar answered Sep 28 '22 18:09

David Heffernan