7

更新: 簡単な回避策。メソッド/演算子フィールドの前のデータフィールド。

今日は、私が作成できる最も簡単な例を使用して、このバグを再現しようとしました。

  • 単純なsetメソッドとprintメソッド(演算子なし)のみを持つ基本レコード(TBasicRecord)から始めましたが、const x:TBasicBecordを渡すのに問題はありませんでした。

  • 次に、バグを引き起こすと考えている単項演算子を追加しましたが、レコードをconstとして渡すことに問題はありません。

  • 次に二項演算子を追加しましたが、それでもバグは表面化しませんでした。

  • 最後に、簡単な例で、メソッドフィールドの前にデータフィールドを宣言していることに気付きました。これで、バグをミュートするために必要なすべてのことがわかりました。

データフィールドもプライベートにしたので、最初はそれが問題だと思っていましたが、結局は関係ないことがわかりました。違いを生む唯一のことは、データフィールドを演算子フィールドとメソッドフィールドの前に配置したかどうかです。

全体的に、私はこの解決策に満足しています。個人的には、とにかくデータフィールドを常に最初に置いてきました。レコード型を「const」パラメータとしてどこにも渡そうとしない限り、逆の方法でそれを行っても他の問題が発生しないように見えるのはおかしいです。


元の投稿:

以前はDelphi7を使用していましたが、今日はDelphi 2006をインストールして、D7がサポートしていない演算子メソッドにアクセスできるようにしました。

ここでの以前の質問への回答の1つにリストされているコード(複素数の実装)をコンパイルしようとしました:TComplexMathクラス(ソースを含む)の簡単な例をリクエストしてください

関連するコードの部分的なリストは次のとおりです。

type
  TComplex = record
  public
    class operator Implicit(const D: Double): TComplex;
    class operator Negative(const C: TComplex): TComplex;
    class operator Equal(const C1, C2: TComplex): Boolean;
    class operator NotEqual(const C1, C2: TComplex): Boolean;
    class operator Add(const C1, C2: TComplex): TComplex;
    class operator Add(const C: TComplex; const D: Double): TComplex;
    class operator Add(const D: Double; const C: TComplex): TComplex;
    class operator Subtract(const C1, C2: TComplex): TComplex;
    class operator Subtract(const C: TComplex; const D: Double): TComplex;
    class operator Subtract(const D: Double; const C: TComplex): TComplex;
    class operator Multiply(const C1, C2: TComplex): TComplex;
    class operator Multiply(const C: TComplex; const D: Double): TComplex;
    class operator Multiply(const D: Double; const C: TComplex): TComplex;
    class operator Divide(const C1, C2: TComplex): TComplex;
    class operator Divide(const C: TComplex; const D: Double): TComplex;
    class operator Divide(const D: Double; const C: TComplex): TComplex;
    function IsZero: Boolean;
    function IsNonZero: Boolean;
    function Conj: TComplex;
    function Sqr: TComplex;
    function Sqrt: TComplex;
    function Mag: Double;
    function SqrMag: Double;
  public
    r: Double;
    c: Double;
  end;

class operator TComplex.Negative(const C: TComplex): TComplex;
begin
  Result.r := -C.r;
  Result.c := -C.c;
end;
---- etc ---

問題は、このコードを(D2006で)コンパイルしようとすると、TComplex型をとるすべての演算子がE2037のエラーを出すことです。「----」の宣言は前の宣言とは異なります。(ここで、「---」は演算子名です)。

私の回避策は、すべてのTComplexパラメーターからconstキーワードを削除するだけで、コードが正しく準拠(および実行)されることでした。「constx:Double」パラメーターを保持できます。コンパイラーはそれらにエラーを出しませんが、他のすべてから「const」を削除する必要がありました。

これが有効になっていないコンパイラオプションであるかどうか誰かが知っていますか?または、これはDelphiの新しいバージョンではサポートされていますが、D2006ではサポートされていませんか?それとも私が何か他のことを間違ってやっているだけですか?

また、ここでconstパラメーターを使用できない場合は、constの代わりにvarを使用するだけの利点がありますconstキーワードを完全に削除する場合と比較して)。

4

2 に答える 2

10

で置き換えてはいけません。理由を説明しましょう。constvar

バックグラウンド

function Add(a: integer): integer;
begin
  result := a + 5;
end;

引数 + 5 を返しますShowMessage(IntToStr(Add(10)))a := 10; ShowMessage(IntToStr(Add(a)))同じ結果を得るために行うこともできます。どちらの場合も、関数に渡されるのAddは数値10です。メッセージには が表示されます15

varパラメータの使用目的は次のとおりです。

procedure Add(var a: integer);
begin
  a := a + 5;
end;

var引数変数を参照渡しする必要があることを示します。つまり、引数変数へのポインターのみをプロシージャー/関数に渡す必要があります。

したがって、今できること

a := 10;
Add(a);
ShowMessage(IntToStr(a)); // You get 15

はまったく変数ではないため、今ではできません。Add(10)10

比べる、

function Add(a: integer): integer;
begin
  a := a + 5;
  result := a;
end;

影響しませんa。そう、

a := 10;
ShowMessage(IntToStr(Add(a))); // You get 15
ShowMessage(IntToStr(a)); // You get 10   

さて、この恐ろしい関数を考えてみましょう:

function Add(var a: integer): integer;
begin
  a := a + 5;
  result := a;
end;

これもその引数 + 5 を返しますが、その引数にも影響を与え (非常に意外なことに!!)、引数として変数以外のものを渡すことはできません (したがって、動作しAdd(10)ません!!)!

a := 10;
ShowMessage(IntToStr(Add(a))); // You get 15
ShowMessage(IntToStr(a)); // You get 15 (!!!)

それで、何constですか?おおざっぱにconst言えば、「可能であれば参照渡し (高速化のため。たとえば、大きなレコードのコピーを作成する必要はありません) を意味しますが、引数への変更は一切受け付けません」という意味です。したがって、const引数は、変更できないことを除いて、通常の引数として効果的に機能します。

function Add(const a: integer): integer;
begin
  result := a + 5;
end;

動作中

function Add(const a: integer): integer;
begin
  a := a + 5;
  result := a;
end;

コンパイルすらしません!しかし、あなたはまだできますAdd(10)

関連するケース

この議論から、 で置き換えるべきではないことは明らかconstですvar。それはそう、

  1. constからに変更すると、関数はリテラル ( ) または式 (または)varの引数を受け入れなくなります。これは主要なショーストッパーです。10Tag + 30SomeFunc(a, b)
  2. 関数の将来の実装では、引数が変更される可能性があり、これにより、引数として渡される変数が誤って変更されます。

最初のポイントの例。constまたは通常の引数を使用する:

function Complex(a, b: real): TComplex;
begin
  result.r := a;
  result.c := b;
end;

...

var
  c, d: TComplex;
begin    
  d := -c;                        // Works!
  d := -Complex(10, 20);          // Works!

しかし、使用var

var
  c, d: TComplex;
begin    
  d := -c;                        // Works!
  d := -Complex(10, 20);          // [DCC Error] Unit5.pas(262):
                                  // E2015 Operator not applicable to this
                                  // operand type

これはどちらも機能しません(でvar):

var
  a, b, c: TComplex;
begin

  a := -(b + c);

実際、 の引数はNegative変数ではなく、式b + cです。だからあなたは非常に失う!

2点目の例。あなたが悪い日を過ごし、toの実装にうんざりしているとしましょNegative

class operator TComplex.Negative(var C: TComplex): TComplex;
begin
  C.r := -C.r;
  C.c := -C.c;
  result := C;
end;

次に、次のコード、

var
  c, d: TComplex;
begin

  c := Complex(10, 20);
  d := -c;

  ShowMessage(FloatToStr(c.r));
  ShowMessage(FloatToStr(d.r));

以前はメッセージ10とが発生していましたが、-10突然変化して が発生します。これは非常に予想外です!-10-10

結論

したがって、あなたの場合の解決策は、単にconst完全に削除することです(!で置き換えないでvarください)。

于 2013-02-28T21:42:39.907 に答える
6

演算子のオーバーロードで const を var に置き換えないでください。限目。

関数の本体内で var パラメーターを変更しないと約束したとしても (そもそも疑わしい根拠です)、var パラメーターが存在するだけで、演算子関数の非常に重要な側面である式の構成が破壊されます。関数の結果を var params に渡すことができないため、演算子関数の var param を使用すると、複合式でその演算子を他の演算子と一緒に構成することができなくなります。

例: (A + B) * C.

A、B、および C がすべて TComplex 型の場合、これは にコンパイルされTComplex.Multiply(TComplex.Add(A, B), C)ます。TComplex.Multiply が var params で宣言されている場合、Add の関数結果を Multiply に渡すことはできません (関数の結果は中間値であり、特定のメモリ アドレスに存在する変数ではないため)。つまり、( A + B) * C はコンパイルされません。

したがって、複合式で演算子を使用できるようにする場合は、演算子関数で var params を使用しないでください。

于 2013-03-01T01:52:54.810 に答える