Naja es ist halt semantisch was komplett anderes. Zum einen kannst du keine Konstanten übergeben, zum anderen kann der wert überschrieben werden, was ja auch eine komplett andere Semantik hat. Zu guter letzt, ists laufzeit Polymorphismus, d.h. es gehen ein paar CPU zyklen flöten.
Beispiel:
Code: Alles auswählen
procedure Foo(x: Integer);
begin
while x > 0 do
begin
WriteLn(x);
Dec(x);
end;
end;
procedure Foo(_: TNullPtr);inline;
begin
Foo(3);
end;
begin
Foo(NullPtr);
Foo(2);
end.
In diesem Fall wird Foo(NullPtr) wegen inlining direkt zu Foo(3) ersetzt, somit ist absolut kein Laufzeit code notwendig um den Default wert zu setzen. Gleichzeitig kann X als normaler value parameter verwendet werden, d.h. Foo hat eine eigene Kopie von x, womit dieses in der Funktion verändert werden kann, ohne das es Seiteneffekt auf die Aufrufende funktion hat.
Zu guter letzt weil es ein call by value ist, kann man die Konstante 2 übergeben, ohne sie vorher in eine Temporäre Variable schreiben zu müssen.
Das beste Beispiel wie sowas nützlich sein kann sind die Unpack funktionen von der Tuples unit:
Code: Alles auswählen
function MyTuple: specialize TTriple<Integer, Char, Double>;
begin
Result := specialize Triple<Integer, Char, Double>(42, 'c', 3.14);
end;
...
var
i: Integer;
c: Char;
d: Double;
begin
MyTuple.Unpack(_, c, d); // Ignoriert den ersten Wert und entpackt die anderen in c und d
MyTuple.Unpack(i, _, _); // Enpackt den ersten wert nach i und ignoriert die anderen
end;
Und wenn du in die Unpack funktionen reinschaust siehst du das da kein einziges if-then-else drin ist, sondern das wird ausschließlich über compiletime polymorphismus gelöst und somit wird kein unnötiger code zur Laufzeit erzeugt