2015-09-16 17 views
5

Warum führt dieser Code nicht zu einem Compilerfehler? Ich hätte beispielsweise einen mehrdeutigen Anruf bei CallMe erwartet. Ist das ein Fehler im Compiler oder in der Sprache? Dies kann unter Verwendung des Gerätenamens und eines Punktes vor dem Funktionsaufruf geschehen, wobei jedoch Benutzercode und Bibliothekscode nicht gegen Namenskollisionen geschützt werden. Du denkst, dass dein Code etwas getan hat, aber es hat etwas anderes gemacht und das ist schlecht.Warum führt das Aufrufen einer Funktion mit identischen Signaturen in verschiedenen Einheiten nicht zu einem Compilerfehler?

uses 
    Unit2, Unit3; 

{$R *.lfm} 
{ TForm1 } 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
    ShowMessage(IntToStr(CallMe(5))); 
end; 

unit Unit2; 
{$mode objfpc}{$H+} 
interface 
uses 
    Classes, SysUtils; 
function CallMe(A: Integer) : Integer; 
implementation 
function CallMe(A: Integer) : Integer; 
begin 
    Result := A * 2; 
end; 
end. 

unit Unit3; 
{$mode objfpc}{$H+} 
interface 
uses 
    Classes, SysUtils; 
function CallMe(A: Integer) : Integer; 
implementation 
function CallMe(A: Integer) : Integer; 
begin 
    Result := A * -1; 
end; 
end. 
+5

Das ist von Entwurf: Es ruft das auf, das der Compiler zuletzt während des Kompilierens gesehen hat. Wenn Sie den anderen anrufen möchten, setzen Sie den Gerätenamen gefolgt von einem Punkt auf den Namen. – MartynA

+0

Danke. Ich würde gerne die Motivation hinter diesem Design wissen. Dies macht eine Chance für Bugs. Nehmen wir an, dass die Haupteinheit ursprünglich CallMe von Unit2 und dann von Programmierer B verwendet hat und Unit3 hinzufügt, weil er eine Funktion von dort benötigt und nicht weiß, dass er versehentlich CallMe durch etwas anderes ersetzt hat (stellen Sie sich eine lange Einheit mit viel Code vor). Es kompiliert und läuft. Keine Warnung, kein Fehler. Ich hätte lieber einen Compilerfehler als ein Laufzeitproblem, und ich möchte keine AVeryLongLibraryName.FunctionName-Aufrufe und jeden Aufruf in jeder enthaltenen Einheit für einen mehrdeutigen Aufruf suchen. –

+1

Jede Programmiersprache bietet eine Möglichkeit für Fehler. Die ganze Sache ist: Sie müssen wissen, was Sie tun. Es gibt externe Tools, die Ihnen Hinweise zu diesen Fällen geben. –

Antwort

13

Von documentation:

Wenn zwei Einheiten eine Variable deklarieren, konstant, Typen, Verfahren oder eine Funktion mit dem gleichen Namen, verwendet der Compiler den man aus dem Gerät zuletzt genannten in der uses-Klausel . (Um die Kennung von der anderen Einheit zuzugreifen, würden Sie einen Qualifizierer hinzufügen:. UnitName.Identifier) ​​

+0

Ich sehe.Ich muss sagen, das war sehr überraschend und meiner Meinung nach gefährliches Verhalten. Eine später hinzugefügte Bibliothek zu einem Projekt hatte eine Funktion mit derselben Signatur wie im alten Code und alter Code begann mit der neuen Funktion, weil die neue Einheit zuletzt in den uses-Klauseln hinzugefügt wurde und die neue Funktion einen Fehler hatte und anders arbeitete verursachte Probleme. Weißt du, kann dies in einen Compiler-Fehler per Option umgewandelt werden, so geschieht dies nicht zufällig wieder? –

+0

Ich habe mit diesem Verhalten seit den TP4 Tagen gelebt und hatte nie wirklich Probleme mit Namenskonflikten. Es gibt keine Hinweis/Warnung/Fehler-Option, um diese Bedingung anzuzeigen. Der Code-Einblick kann jedoch hilfreich sein und zeigen, zu welcher Einheit die Funktionen gehören. –

+0

@ user2304430: Es gibt keine Möglichkeit, es zu einem Fehler zu machen, weil es kein Fehler ist. –

2

Wie gesagt, es ist von Entwurf, die Compiler Lasten Symbole von Einheiten eines Stapel basierten Ansatz und Parsen durch den Stapel von zuletzt geladen bis zuerst geladen, um nach einem Symbol zu suchen. Der Preprozessor-Status wird jedoch direkt in den globalen Status eingefügt.

Cross-Unit-Überladung ist jedoch eine Ausnahme. Wenn Sie beide Funktionen mit Überlast markieren; Direktive, erhalten Sie einen Fehler (bla war der Name der Funktion im Test)

[dcc32 Error] test.dpr: E2251 Ambiguous overloaded call to 'bla' 
    Unit1.pas(8): Related method: procedure bla; 
    Unit2.pas(8): Related method: procedure bla; 

, wenn Sie zwei unterschiedliche Signaturen haben, wird es die besten passende auswählen.

Cross-Overloading ist eine neuere Funktion, aber ich erinnere mich nicht genau wann. Meine Vermutung ist D2006.