Overloading in Pascal, with or without the "overload" keyword? - michaliskambi/modern-pascal-introduction GitHub Wiki

Both Delphi and FPC allow to overload with the overload keyword.

FPC allows to also overload things without using the overload keyword.

Their practical end-result is similar, though not exactly equal. That is, you want to enable multiple ways how your routine (method or global function / procedure). So you just write this:

type
  TMyClass = class
    procedure DoSomething(const I: Integer);  overload;
    procedure DoSomething(const S: String); overload;
  end;

procedure TMyClass.DoSomething(const I: Integer); 
begin
  Writeln('Got int', I);
end;

procedure TMyClass.DoSomething(const S: String);
begin
  Writeln('Got string', S);
end;

var
  O: TMyClass;
begin
  O := TMyClass.Create;
  try
    O.DoSomething(123);
    O.DoSomething('hello');
  finally FreeAndNil(O) end;
end.

In FPC, you can remove overload above, and it will work the same.

However, the difference between these 2 overloading ways in FPC is actually more impactful.

Technically speaking,

  • Overloading with overload means you add a new way of calling given routine to the namespace. This is added to how the routine could be called according to ancestor classes (in case of methods) or other units (in case of global functions / procedures).

  • Overloading without overload means that you introduce a collection of overloads in this class or unit. Other code can call any of the overloads, but they "obscure" overloads in ancestor classes (in case of methods) or other units (in case of global functions / procedures).

You can say the first way of overloading (with overload) is cross-class and cross-unit. The 2nd way (without overload) is not cross-class and cross-unit.

As an example when cross-unit overloading makes sense: You can have a unit 'CastleMath` that defines

function Lerp(const A, B: Single; const T: Single): Single; overload;
function Lerp(const A, B: Double; const T: Single): Double; overload;

And another unit CastleVectors that defines

function Lerp(const A, B: TVector2; const T: Single): TVector2; overload;
function Lerp(const A, B: TVector3; const T: Single): TVector3; overload;

And if your code uses both CastleMath, CastleVectors than all 4 Lerp overloads are available. It's great in this case.

There are however cases when this cross-class and cross-unit is not desirable. The most prominent is an issue when overloading constructors with overload, and thus accidentally allowing user to call a parameterless constructor of the ancestor. Consider this:

// THIS CODE HAS A TRAP, DON'T DO THIS!
type
  TMyClass = class
    constructor Create;
  end;

  TMyClassDescendant = class(TMyClass);
    constructor Create(const I: Integer); overload;
    constructor Create(const S: String); overload;
  end;

var
  D: TMyClassDescendant;
begin
  // This *compiles* but it's a trap...
  // You created TMyClassDescendant instance, without calling code of any TMyClassDescendant constructor.
  // Only TMyClass.Create got called, the parameterless constructor.
  // Actually, this example would also make sense without TMyClass, because TObject itself has parameterless constructor.
  D := TMyClassDescendant.Create;
  D.Free;
end;

See for more info:

type
  { Descend from this to hide a parameterless constructor.

    In Delphi, if you have multiple overloaded constructors (marked
    with "overload"), the user can call any constructor of the ancestor
    class that you have not "obscured" by a new definition.
    In particular, if all your constructors have some parameters,
    then user can "bypass" them by calling a parameterless constructor
    defined by TObject.
    Descend from this class to disallow this.
    See
    https://stackoverflow.com/questions/14003153/how-to-hide-the-inherited-tobject-constructor-while-the-class-has-overloaded-one
    http://andy.jgknet.de/blog/2011/07/hiding-the-tobject-create-constructor/

    Note that in FPC (in ObjFpc mode) this is not a problem,
    as the standard overloading (without the "overload" keyword) is more sane:
    the overloaded constructors obscure the ancestor constructors. }
  {$warnings off}
  TNoParameterlessContructor = class
  strict private
    constructor Create;
  end;
  {$warnings on}