Why I can't pass 'Child' class instance when switching parameter type from 'const' to 'var' in 'overloaded' methoduac elevate while using ifileoperation copyitemclient side web serviceTNetSharingManager access violation problemStrange “A component named TFrm1 already exists” errorDelphi Rtti for interfaces in a generic contextHow to export Overload functions from DLL?In Delphi, are parameters evaluated in order when passed into a method?Delphi XE3 Invalid Pointer when trying to free FSQL (TStringList)How to pass method type as parameter?How do I convert an enum to string and back again using RTTI in Delphi

What is the most remote airport from the center of the city it supposedly serves?

Independent, post-Brexit Scotland - would there be a hard border with England?

How wide is a neg symbol, how to get the width for alignment?

Out of scope work duties and resignation

Does a card have a keyword if it has the same effect as said keyword?

Has a commercial or military jet bi-plane ever been manufactured?

How to model the curly cable part of the phone

Manager is threatening to grade me poorly if I don't complete the project

Should I replace my bicycle tires if they have not been inflated in multiple years

Can my company stop me from working overtime?

How long would it take for people to notice a mass disappearance?

I drew a randomly colored grid of points with tikz, how do I force it to remember the first grid from then on?

how to ban all connection to .se and .ru in the hosts.deny-file

Why wasn't the Night King naked in S08E03?

Do Maps have an Reliable Relationship between keySet() order and values() order?

Why do people keep telling me that I am a bad photographer?

Why do money exchangers give different rates to different bills?

Why is Arya visibly scared in the library in S8E3?

Getting a W on your transcript for grad school applications

I need a disease

Are there any Final Fantasy Spirits in Super Smash Bros Ultimate?

What does this colon mean? It is not labeling, it is not ternary operator

Why is B♯ higher than C♭ in 31-ET?

Where can I go to avoid planes overhead?



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


uac elevate while using ifileoperation copyitemclient side web serviceTNetSharingManager access violation problemStrange “A component named TFrm1 already exists” errorDelphi Rtti for interfaces in a generic contextHow to export Overload functions from DLL?In Delphi, are parameters evaluated in order when passed into a method?Delphi XE3 Invalid Pointer when trying to free FSQL (TStringList)How to pass method type as parameter?How do I convert an enum to string and back again using RTTI in Delphi






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








5















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(constvar 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?










share|improve this question



















  • 1





    Overloads are picky sometimes, you can cast it to an animal: Train(TAnimal(Dog));

    – Sertac Akyuz
    Mar 22 at 22:20












  • @SertacAkyuz or declare it as TAnimal

    – Nasreddine Galfout
    Mar 22 at 22:21






  • 4





    That actually works. You can declare Dog as a TAnimal, even if it contains a TDog instance. The main question is: do you want to change the instance of the animal, so for instance you put in a dog, train it, and get back a cat? Or nil, as in your code.. "Sorry mam, I tried to train your dog, but it died. Here is nil in return") If you don't want that, remove var, stick with const (or not), and you'll be fine. You can then still change the properties of the animal you train, but not the animal itself...

    – GolezTrol
    Mar 22 at 23:09







  • 1





    That depends on if what you call is virtual and the class overrides it. ... In the end, this is a bug.

    – Sertac Akyuz
    Mar 22 at 23:24






  • 2





    @NasreddineGalfout calling Free on a base class pointer is fine, since TObject.Destroy is virtual. This is a key requirement of polymorphism to allow derived destructors to be called properly.

    – Remy Lebeau
    Mar 23 at 1:22


















5















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(constvar 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?










share|improve this question



















  • 1





    Overloads are picky sometimes, you can cast it to an animal: Train(TAnimal(Dog));

    – Sertac Akyuz
    Mar 22 at 22:20












  • @SertacAkyuz or declare it as TAnimal

    – Nasreddine Galfout
    Mar 22 at 22:21






  • 4





    That actually works. You can declare Dog as a TAnimal, even if it contains a TDog instance. The main question is: do you want to change the instance of the animal, so for instance you put in a dog, train it, and get back a cat? Or nil, as in your code.. "Sorry mam, I tried to train your dog, but it died. Here is nil in return") If you don't want that, remove var, stick with const (or not), and you'll be fine. You can then still change the properties of the animal you train, but not the animal itself...

    – GolezTrol
    Mar 22 at 23:09







  • 1





    That depends on if what you call is virtual and the class overrides it. ... In the end, this is a bug.

    – Sertac Akyuz
    Mar 22 at 23:24






  • 2





    @NasreddineGalfout calling Free on a base class pointer is fine, since TObject.Destroy is virtual. This is a key requirement of polymorphism to allow derived destructors to be called properly.

    – Remy Lebeau
    Mar 23 at 1:22














5












5








5








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(constvar 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?










share|improve this question
















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(constvar 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?







delphi delphi-10.2-tokyo






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 22 at 22:24







Nasreddine Galfout

















asked Mar 22 at 22:13









Nasreddine GalfoutNasreddine Galfout

1,5832728




1,5832728







  • 1





    Overloads are picky sometimes, you can cast it to an animal: Train(TAnimal(Dog));

    – Sertac Akyuz
    Mar 22 at 22:20












  • @SertacAkyuz or declare it as TAnimal

    – Nasreddine Galfout
    Mar 22 at 22:21






  • 4





    That actually works. You can declare Dog as a TAnimal, even if it contains a TDog instance. The main question is: do you want to change the instance of the animal, so for instance you put in a dog, train it, and get back a cat? Or nil, as in your code.. "Sorry mam, I tried to train your dog, but it died. Here is nil in return") If you don't want that, remove var, stick with const (or not), and you'll be fine. You can then still change the properties of the animal you train, but not the animal itself...

    – GolezTrol
    Mar 22 at 23:09







  • 1





    That depends on if what you call is virtual and the class overrides it. ... In the end, this is a bug.

    – Sertac Akyuz
    Mar 22 at 23:24






  • 2





    @NasreddineGalfout calling Free on a base class pointer is fine, since TObject.Destroy is virtual. This is a key requirement of polymorphism to allow derived destructors to be called properly.

    – Remy Lebeau
    Mar 23 at 1:22













  • 1





    Overloads are picky sometimes, you can cast it to an animal: Train(TAnimal(Dog));

    – Sertac Akyuz
    Mar 22 at 22:20












  • @SertacAkyuz or declare it as TAnimal

    – Nasreddine Galfout
    Mar 22 at 22:21






  • 4





    That actually works. You can declare Dog as a TAnimal, even if it contains a TDog instance. The main question is: do you want to change the instance of the animal, so for instance you put in a dog, train it, and get back a cat? Or nil, as in your code.. "Sorry mam, I tried to train your dog, but it died. Here is nil in return") If you don't want that, remove var, stick with const (or not), and you'll be fine. You can then still change the properties of the animal you train, but not the animal itself...

    – GolezTrol
    Mar 22 at 23:09







  • 1





    That depends on if what you call is virtual and the class overrides it. ... In the end, this is a bug.

    – Sertac Akyuz
    Mar 22 at 23:24






  • 2





    @NasreddineGalfout calling Free on a base class pointer is fine, since TObject.Destroy is virtual. This is a key requirement of polymorphism to allow derived destructors to be called properly.

    – Remy Lebeau
    Mar 23 at 1:22








1




1





Overloads are picky sometimes, you can cast it to an animal: Train(TAnimal(Dog));

– Sertac Akyuz
Mar 22 at 22:20






Overloads are picky sometimes, you can cast it to an animal: Train(TAnimal(Dog));

– Sertac Akyuz
Mar 22 at 22:20














@SertacAkyuz or declare it as TAnimal

– Nasreddine Galfout
Mar 22 at 22:21





@SertacAkyuz or declare it as TAnimal

– Nasreddine Galfout
Mar 22 at 22:21




4




4





That actually works. You can declare Dog as a TAnimal, even if it contains a TDog instance. The main question is: do you want to change the instance of the animal, so for instance you put in a dog, train it, and get back a cat? Or nil, as in your code.. "Sorry mam, I tried to train your dog, but it died. Here is nil in return") If you don't want that, remove var, stick with const (or not), and you'll be fine. You can then still change the properties of the animal you train, but not the animal itself...

– GolezTrol
Mar 22 at 23:09






That actually works. You can declare Dog as a TAnimal, even if it contains a TDog instance. The main question is: do you want to change the instance of the animal, so for instance you put in a dog, train it, and get back a cat? Or nil, as in your code.. "Sorry mam, I tried to train your dog, but it died. Here is nil in return") If you don't want that, remove var, stick with const (or not), and you'll be fine. You can then still change the properties of the animal you train, but not the animal itself...

– GolezTrol
Mar 22 at 23:09





1




1





That depends on if what you call is virtual and the class overrides it. ... In the end, this is a bug.

– Sertac Akyuz
Mar 22 at 23:24





That depends on if what you call is virtual and the class overrides it. ... In the end, this is a bug.

– Sertac Akyuz
Mar 22 at 23:24




2




2





@NasreddineGalfout calling Free on a base class pointer is fine, since TObject.Destroy is virtual. This is a key requirement of polymorphism to allow derived destructors to be called properly.

– Remy Lebeau
Mar 23 at 1:22






@NasreddineGalfout calling Free on a base class pointer is fine, since TObject.Destroy is virtual. This is a key requirement of polymorphism to allow derived destructors to be called properly.

– Remy Lebeau
Mar 23 at 1:22













2 Answers
2






active

oldest

votes


















8















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.






share|improve this answer























  • this really cleared things in my mind.

    – Nasreddine Galfout
    Mar 23 at 11:00











  • one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

    – Nasreddine Galfout
    Mar 23 at 11:07












  • Because the caller can't see the modification

    – David Heffernan
    Mar 23 at 11:32











  • I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

    – Sertac Akyuz
    Mar 23 at 11:42







  • 1





    @Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

    – David Heffernan
    Mar 23 at 14:16


















3














Basic issue with non matching var parameters is the possibility that you end up with wrong type inside the calling variable.



You can trick the compiler to do that by using absolute keyword - that allows you to declare variables of different type that share same space - and simulate what would happen if compiler would allow you to use such construct.



Consider following example



uses
System.SysUtils;

type
TAnimal = class
public
procedure Run; virtual;
end;

TDog = class(TAnimal)
public
procedure Bark; virtual;
procedure Fetch; virtual;
end;

TCat = class(TAnimal)
public
procedure Meow; virtual;
end;

procedure TAnimal.Run;
begin
Writeln('Run');
end;

procedure TDog.Bark;
begin
Writeln('Bark');
end;

procedure TDog.Fetch;
begin
Writeln('Fetch');
end;

procedure TCat.Meow;
begin
Writeln('Meow');
end;

procedure Move(const aA: TAnimal);
begin
aA.Run;
end;

procedure Train(var aA: TAnimal);
begin
aA := TCat.Create;
end;

var
Dog: TDog;
Cat: TAnimal absolute Dog;
begin
try
// we cannot use Dog here, because compiler would refuse to compile such code
// Cat is TAnimal and compiler allows to pass it
// since Dog and Cat variables share same address space that is
// equivalent of calling Train(Dog);
Train(Cat);

Move(Cat);
Dog.Bark;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.


If you run above code you will get following output



Run
Meow


Dog and Cat variables share the same address space, so when you call Train(Cat) as the result you will get TCat instance that you can use either through Cat or Dog variable. Basically, you will end up with TCat instance inside TDog variable.



Clearly, when you call Dog.Bark, you should get Bark as output not Meow. Meow is the first method in TCat just like the Bark is the first method in TDog, and when resolving the Bark address through TCat virtual method table it will find Meow method instead. Since both methods have same signature everything is fine if you consider wrong output as being fine.



Now, if you try calling the Dog.Fetch, the application will crash with AV. There are no matching methods at corresponding address in TCat class and you are basically calling some uninitialized place in memory instead of proper method.



That explains why var or out parameter types must match the caller variable type.



As to why you can pass TDog or TCat as TAnimal const or value parameter. Both TDog and TCat inherit from TAnimal and whatewer you can do with TAnimal instance, both TDog and TCat support it. They can override particular behavior, so your cat can run differently than your dog, but whatever you do it is well defined. You cannot end up running some inexistent code.



procedure Move(const aA: TAnimal);
begin
aA.Run;
aA.Fetch; // this will fail to compile - there is no Fetch method in TAnimal class
end;


Of course, this does not prevent you to test for particular class and use type casts to call Fetch if TAnimal actually is a TDog.



procedure Move(const aA: TAnimal);
begin
aA.Run;
if aA is TDog then TDog(aA).Fetch;
end;


However, if you abuse typecasting and typecast without checking whether particular variable is actually a TDog instance, you will again trip into AV.



procedure Move(const aA: TAnimal);
begin
aA.Run;
TDog(aA).Fetch;
end;





share|improve this answer

























  • Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

    – Nasreddine Galfout
    Mar 23 at 23:05












Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55308448%2fwhy-i-cant-pass-child-class-instance-when-switching-parameter-type-from-cons%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









8















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.






share|improve this answer























  • this really cleared things in my mind.

    – Nasreddine Galfout
    Mar 23 at 11:00











  • one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

    – Nasreddine Galfout
    Mar 23 at 11:07












  • Because the caller can't see the modification

    – David Heffernan
    Mar 23 at 11:32











  • I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

    – Sertac Akyuz
    Mar 23 at 11:42







  • 1





    @Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

    – David Heffernan
    Mar 23 at 14:16















8















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.






share|improve this answer























  • this really cleared things in my mind.

    – Nasreddine Galfout
    Mar 23 at 11:00











  • one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

    – Nasreddine Galfout
    Mar 23 at 11:07












  • Because the caller can't see the modification

    – David Heffernan
    Mar 23 at 11:32











  • I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

    – Sertac Akyuz
    Mar 23 at 11:42







  • 1





    @Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

    – David Heffernan
    Mar 23 at 14:16













8












8








8








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.






share|improve this answer














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.







share|improve this answer












share|improve this answer



share|improve this answer










answered Mar 23 at 10:54









David HeffernanDavid Heffernan

524k358431234




524k358431234












  • this really cleared things in my mind.

    – Nasreddine Galfout
    Mar 23 at 11:00











  • one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

    – Nasreddine Galfout
    Mar 23 at 11:07












  • Because the caller can't see the modification

    – David Heffernan
    Mar 23 at 11:32











  • I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

    – Sertac Akyuz
    Mar 23 at 11:42







  • 1





    @Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

    – David Heffernan
    Mar 23 at 14:16

















  • this really cleared things in my mind.

    – Nasreddine Galfout
    Mar 23 at 11:00











  • one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

    – Nasreddine Galfout
    Mar 23 at 11:07












  • Because the caller can't see the modification

    – David Heffernan
    Mar 23 at 11:32











  • I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

    – Sertac Akyuz
    Mar 23 at 11:42







  • 1





    @Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

    – David Heffernan
    Mar 23 at 14:16
















this really cleared things in my mind.

– Nasreddine Galfout
Mar 23 at 11:00





this really cleared things in my mind.

– Nasreddine Galfout
Mar 23 at 11:00













one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

– Nasreddine Galfout
Mar 23 at 11:07






one more thing why the compiler accepts it if I remove the 'var', I mean I can still do the above. (in my mind when adding 'var' the compiler will not create a second pointer to the first pointer to Dog, I think I read this in one of your answers)

– Nasreddine Galfout
Mar 23 at 11:07














Because the caller can't see the modification

– David Heffernan
Mar 23 at 11:32





Because the caller can't see the modification

– David Heffernan
Mar 23 at 11:32













I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

– Sertac Akyuz
Mar 23 at 11:42






I don't understand why the example you give is relevant, the code in the question tries to pass a dog as an animal. If you test if a dog is an animal, it is. ... You would probably argue then, the behavior when you don't use the var is a compiler defect, you shouldn't be able to pass a dog as an animal...

– Sertac Akyuz
Mar 23 at 11:42





1




1





@Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

– David Heffernan
Mar 23 at 14:16





@Sertac that fails because an animal can be passed in. The example in the question fails because an animal can be passed out.

– David Heffernan
Mar 23 at 14:16













3














Basic issue with non matching var parameters is the possibility that you end up with wrong type inside the calling variable.



You can trick the compiler to do that by using absolute keyword - that allows you to declare variables of different type that share same space - and simulate what would happen if compiler would allow you to use such construct.



Consider following example



uses
System.SysUtils;

type
TAnimal = class
public
procedure Run; virtual;
end;

TDog = class(TAnimal)
public
procedure Bark; virtual;
procedure Fetch; virtual;
end;

TCat = class(TAnimal)
public
procedure Meow; virtual;
end;

procedure TAnimal.Run;
begin
Writeln('Run');
end;

procedure TDog.Bark;
begin
Writeln('Bark');
end;

procedure TDog.Fetch;
begin
Writeln('Fetch');
end;

procedure TCat.Meow;
begin
Writeln('Meow');
end;

procedure Move(const aA: TAnimal);
begin
aA.Run;
end;

procedure Train(var aA: TAnimal);
begin
aA := TCat.Create;
end;

var
Dog: TDog;
Cat: TAnimal absolute Dog;
begin
try
// we cannot use Dog here, because compiler would refuse to compile such code
// Cat is TAnimal and compiler allows to pass it
// since Dog and Cat variables share same address space that is
// equivalent of calling Train(Dog);
Train(Cat);

Move(Cat);
Dog.Bark;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.


If you run above code you will get following output



Run
Meow


Dog and Cat variables share the same address space, so when you call Train(Cat) as the result you will get TCat instance that you can use either through Cat or Dog variable. Basically, you will end up with TCat instance inside TDog variable.



Clearly, when you call Dog.Bark, you should get Bark as output not Meow. Meow is the first method in TCat just like the Bark is the first method in TDog, and when resolving the Bark address through TCat virtual method table it will find Meow method instead. Since both methods have same signature everything is fine if you consider wrong output as being fine.



Now, if you try calling the Dog.Fetch, the application will crash with AV. There are no matching methods at corresponding address in TCat class and you are basically calling some uninitialized place in memory instead of proper method.



That explains why var or out parameter types must match the caller variable type.



As to why you can pass TDog or TCat as TAnimal const or value parameter. Both TDog and TCat inherit from TAnimal and whatewer you can do with TAnimal instance, both TDog and TCat support it. They can override particular behavior, so your cat can run differently than your dog, but whatever you do it is well defined. You cannot end up running some inexistent code.



procedure Move(const aA: TAnimal);
begin
aA.Run;
aA.Fetch; // this will fail to compile - there is no Fetch method in TAnimal class
end;


Of course, this does not prevent you to test for particular class and use type casts to call Fetch if TAnimal actually is a TDog.



procedure Move(const aA: TAnimal);
begin
aA.Run;
if aA is TDog then TDog(aA).Fetch;
end;


However, if you abuse typecasting and typecast without checking whether particular variable is actually a TDog instance, you will again trip into AV.



procedure Move(const aA: TAnimal);
begin
aA.Run;
TDog(aA).Fetch;
end;





share|improve this answer

























  • Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

    – Nasreddine Galfout
    Mar 23 at 23:05
















3














Basic issue with non matching var parameters is the possibility that you end up with wrong type inside the calling variable.



You can trick the compiler to do that by using absolute keyword - that allows you to declare variables of different type that share same space - and simulate what would happen if compiler would allow you to use such construct.



Consider following example



uses
System.SysUtils;

type
TAnimal = class
public
procedure Run; virtual;
end;

TDog = class(TAnimal)
public
procedure Bark; virtual;
procedure Fetch; virtual;
end;

TCat = class(TAnimal)
public
procedure Meow; virtual;
end;

procedure TAnimal.Run;
begin
Writeln('Run');
end;

procedure TDog.Bark;
begin
Writeln('Bark');
end;

procedure TDog.Fetch;
begin
Writeln('Fetch');
end;

procedure TCat.Meow;
begin
Writeln('Meow');
end;

procedure Move(const aA: TAnimal);
begin
aA.Run;
end;

procedure Train(var aA: TAnimal);
begin
aA := TCat.Create;
end;

var
Dog: TDog;
Cat: TAnimal absolute Dog;
begin
try
// we cannot use Dog here, because compiler would refuse to compile such code
// Cat is TAnimal and compiler allows to pass it
// since Dog and Cat variables share same address space that is
// equivalent of calling Train(Dog);
Train(Cat);

Move(Cat);
Dog.Bark;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.


If you run above code you will get following output



Run
Meow


Dog and Cat variables share the same address space, so when you call Train(Cat) as the result you will get TCat instance that you can use either through Cat or Dog variable. Basically, you will end up with TCat instance inside TDog variable.



Clearly, when you call Dog.Bark, you should get Bark as output not Meow. Meow is the first method in TCat just like the Bark is the first method in TDog, and when resolving the Bark address through TCat virtual method table it will find Meow method instead. Since both methods have same signature everything is fine if you consider wrong output as being fine.



Now, if you try calling the Dog.Fetch, the application will crash with AV. There are no matching methods at corresponding address in TCat class and you are basically calling some uninitialized place in memory instead of proper method.



That explains why var or out parameter types must match the caller variable type.



As to why you can pass TDog or TCat as TAnimal const or value parameter. Both TDog and TCat inherit from TAnimal and whatewer you can do with TAnimal instance, both TDog and TCat support it. They can override particular behavior, so your cat can run differently than your dog, but whatever you do it is well defined. You cannot end up running some inexistent code.



procedure Move(const aA: TAnimal);
begin
aA.Run;
aA.Fetch; // this will fail to compile - there is no Fetch method in TAnimal class
end;


Of course, this does not prevent you to test for particular class and use type casts to call Fetch if TAnimal actually is a TDog.



procedure Move(const aA: TAnimal);
begin
aA.Run;
if aA is TDog then TDog(aA).Fetch;
end;


However, if you abuse typecasting and typecast without checking whether particular variable is actually a TDog instance, you will again trip into AV.



procedure Move(const aA: TAnimal);
begin
aA.Run;
TDog(aA).Fetch;
end;





share|improve this answer

























  • Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

    – Nasreddine Galfout
    Mar 23 at 23:05














3












3








3







Basic issue with non matching var parameters is the possibility that you end up with wrong type inside the calling variable.



You can trick the compiler to do that by using absolute keyword - that allows you to declare variables of different type that share same space - and simulate what would happen if compiler would allow you to use such construct.



Consider following example



uses
System.SysUtils;

type
TAnimal = class
public
procedure Run; virtual;
end;

TDog = class(TAnimal)
public
procedure Bark; virtual;
procedure Fetch; virtual;
end;

TCat = class(TAnimal)
public
procedure Meow; virtual;
end;

procedure TAnimal.Run;
begin
Writeln('Run');
end;

procedure TDog.Bark;
begin
Writeln('Bark');
end;

procedure TDog.Fetch;
begin
Writeln('Fetch');
end;

procedure TCat.Meow;
begin
Writeln('Meow');
end;

procedure Move(const aA: TAnimal);
begin
aA.Run;
end;

procedure Train(var aA: TAnimal);
begin
aA := TCat.Create;
end;

var
Dog: TDog;
Cat: TAnimal absolute Dog;
begin
try
// we cannot use Dog here, because compiler would refuse to compile such code
// Cat is TAnimal and compiler allows to pass it
// since Dog and Cat variables share same address space that is
// equivalent of calling Train(Dog);
Train(Cat);

Move(Cat);
Dog.Bark;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.


If you run above code you will get following output



Run
Meow


Dog and Cat variables share the same address space, so when you call Train(Cat) as the result you will get TCat instance that you can use either through Cat or Dog variable. Basically, you will end up with TCat instance inside TDog variable.



Clearly, when you call Dog.Bark, you should get Bark as output not Meow. Meow is the first method in TCat just like the Bark is the first method in TDog, and when resolving the Bark address through TCat virtual method table it will find Meow method instead. Since both methods have same signature everything is fine if you consider wrong output as being fine.



Now, if you try calling the Dog.Fetch, the application will crash with AV. There are no matching methods at corresponding address in TCat class and you are basically calling some uninitialized place in memory instead of proper method.



That explains why var or out parameter types must match the caller variable type.



As to why you can pass TDog or TCat as TAnimal const or value parameter. Both TDog and TCat inherit from TAnimal and whatewer you can do with TAnimal instance, both TDog and TCat support it. They can override particular behavior, so your cat can run differently than your dog, but whatever you do it is well defined. You cannot end up running some inexistent code.



procedure Move(const aA: TAnimal);
begin
aA.Run;
aA.Fetch; // this will fail to compile - there is no Fetch method in TAnimal class
end;


Of course, this does not prevent you to test for particular class and use type casts to call Fetch if TAnimal actually is a TDog.



procedure Move(const aA: TAnimal);
begin
aA.Run;
if aA is TDog then TDog(aA).Fetch;
end;


However, if you abuse typecasting and typecast without checking whether particular variable is actually a TDog instance, you will again trip into AV.



procedure Move(const aA: TAnimal);
begin
aA.Run;
TDog(aA).Fetch;
end;





share|improve this answer















Basic issue with non matching var parameters is the possibility that you end up with wrong type inside the calling variable.



You can trick the compiler to do that by using absolute keyword - that allows you to declare variables of different type that share same space - and simulate what would happen if compiler would allow you to use such construct.



Consider following example



uses
System.SysUtils;

type
TAnimal = class
public
procedure Run; virtual;
end;

TDog = class(TAnimal)
public
procedure Bark; virtual;
procedure Fetch; virtual;
end;

TCat = class(TAnimal)
public
procedure Meow; virtual;
end;

procedure TAnimal.Run;
begin
Writeln('Run');
end;

procedure TDog.Bark;
begin
Writeln('Bark');
end;

procedure TDog.Fetch;
begin
Writeln('Fetch');
end;

procedure TCat.Meow;
begin
Writeln('Meow');
end;

procedure Move(const aA: TAnimal);
begin
aA.Run;
end;

procedure Train(var aA: TAnimal);
begin
aA := TCat.Create;
end;

var
Dog: TDog;
Cat: TAnimal absolute Dog;
begin
try
// we cannot use Dog here, because compiler would refuse to compile such code
// Cat is TAnimal and compiler allows to pass it
// since Dog and Cat variables share same address space that is
// equivalent of calling Train(Dog);
Train(Cat);

Move(Cat);
Dog.Bark;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.


If you run above code you will get following output



Run
Meow


Dog and Cat variables share the same address space, so when you call Train(Cat) as the result you will get TCat instance that you can use either through Cat or Dog variable. Basically, you will end up with TCat instance inside TDog variable.



Clearly, when you call Dog.Bark, you should get Bark as output not Meow. Meow is the first method in TCat just like the Bark is the first method in TDog, and when resolving the Bark address through TCat virtual method table it will find Meow method instead. Since both methods have same signature everything is fine if you consider wrong output as being fine.



Now, if you try calling the Dog.Fetch, the application will crash with AV. There are no matching methods at corresponding address in TCat class and you are basically calling some uninitialized place in memory instead of proper method.



That explains why var or out parameter types must match the caller variable type.



As to why you can pass TDog or TCat as TAnimal const or value parameter. Both TDog and TCat inherit from TAnimal and whatewer you can do with TAnimal instance, both TDog and TCat support it. They can override particular behavior, so your cat can run differently than your dog, but whatever you do it is well defined. You cannot end up running some inexistent code.



procedure Move(const aA: TAnimal);
begin
aA.Run;
aA.Fetch; // this will fail to compile - there is no Fetch method in TAnimal class
end;


Of course, this does not prevent you to test for particular class and use type casts to call Fetch if TAnimal actually is a TDog.



procedure Move(const aA: TAnimal);
begin
aA.Run;
if aA is TDog then TDog(aA).Fetch;
end;


However, if you abuse typecasting and typecast without checking whether particular variable is actually a TDog instance, you will again trip into AV.



procedure Move(const aA: TAnimal);
begin
aA.Run;
TDog(aA).Fetch;
end;






share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 24 at 11:34

























answered Mar 23 at 22:38









Dalija PrasnikarDalija Prasnikar

19.1k1054109




19.1k1054109












  • Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

    – Nasreddine Galfout
    Mar 23 at 23:05


















  • Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

    – Nasreddine Galfout
    Mar 23 at 23:05

















Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

– Nasreddine Galfout
Mar 23 at 23:05






Great answer, I should ask more questions like this, you guys are gold when you start talking about pets and how to train them. big fan of the book by the way.

– Nasreddine Galfout
Mar 23 at 23:05


















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55308448%2fwhy-i-cant-pass-child-class-instance-when-switching-parameter-type-from-cons%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript