Swanman's Horizon

性欲をもてあましつつなんらかの話をするよ。

docwikiでは教えてくれない演算子オーバーロード。

In演算子

Inをオーバーロードすると、集合演算子としてのinの動作を定義できるようになる*1

// サンプル
type
  TStrRec = record
  public
    Value: TArray<string>;
    class operator In(const A: string; const B: TStrRec): Boolean;
  end;

class operator TStrRec.In(const A: string; const B: TStrRec): Boolean;
var
  s: string;
begin
  for s in B.Value do
    if s = A then Exit(True);
  Result := False;
end;

// 使い方
var
  str: TStrRec;
begin
  str.Value := TArray<string>.Create('わはー', '!');
  if 'ほげ' in str then
    Writeln('あった'); // 条件がFalseとなり実行されない
  if 'わはー' in str then
    Writeln('あった'); // 条件がTrueとなり実行される
end;

IncludeとExclude手続き

これも集合を操作するInclude及びExlude手続きのオーバーロード。元々これらの手続きはあってもあまり使わないから*2オーバーロード時には何か別の目的を持たせないとそのままでは使い道がなさそう。

// サンプル
type
  TFoobar = (foo, bar, baz);
  TIncludeExclude = record
    Value: set of TFoobar;
    class operator Include(a: TIncludeExclude; b: TFoobar): TIncludeExclude;
    class operator Exclude(a: TIncludeExclude; b: TFoobar): TIncludeExclude;
  end;

class operator TIncludeExclude.Include(a: TIncludeExclude; b: TFoobar): TIncludeExclude;
begin
  Result := a;
  Include(Result.Value, b);
end;

class operator TIncludeExclude.Exclude(a: TIncludeExclude; b: TFoobar): TIncludeExclude;
begin
  Result := a;
  Exclude(Result.Value, b);
end;

// 使い方
var
  ie: TIncludeExclude;
begin
  ie.Value := [];
  Include(ie, foo);
  Include(ie, baz);
  Exclude(ie, foo);
  Include(ie, bar);
end;

TrueとFalse

Trueをオーバーロードすると、論理値として使えるようになる。

// サンプル
type
  TBoolean = record
    Value: Integer;
    class operator True(a: TBoolean): Boolean;
  end;
class operator TBoolean.True(a: TBoolean): Boolean;
begin
  Result := a.Value <> 0;
end;

// 使い方
var
  b: TBoolean;
begin
  b.Value := 0;
  if b then
    Writeln('True'); // 条件がFalseとなり実行されない
  b.Value := 1;
  if b then
    Writeln('True'); // 条件がTrueとなり実行される
end;

ただ、Falseの方は用途が不明。Falseのみ、あるいはTrueとFalse両方をオーバーロードした場合を試したけど、どちらもFalse側の処理は呼ばれなかった。あと、if not b thenとするとLogicalNotを見に行くのか、「この型には指定した演算子は使えません」というエラーが出た。

OnesComplement演算子

これは名前から分かるようにビット反転用と思われるが、書いても動かなかった。そもそもビット反転にはBitwiseNotがすでにあること、及びこれと上記のFalseがうまく動作しないことから考えると、C++Builder用のものか?*3

見つけた経緯

例えばTStringListで中に特定の文字列が含まれてるか知りたいとき、

var
  sl: TStringList;
begin
  if sl.IndexOf('わはー') <> -1 then
    ...
end;

じゃなくて、

var
  sl: TStringList;
begin
  if 'わはー' in sl then
    ...
end;

みたいに書けたら楽だなーとか思ってて、何気なくbinフォルダの中身に'GreaterThanOrEqual'*4でbgrepしたら見つけた的な。

ちなみに

ググってみたところ当たり前ながらすでに発見してた人はいたみたい。
https://forums.embarcadero.com/thread.jspa?messageID=197242&tstart=0

*1:for-inのinではない

*2:Include(a, b)はa := a + [b]で代用できるし

*3:Delphiは論理否定もビット反転も同じnot演算子だけど、C++は!と~で分かれてるってのもあるし

*4:IncとかAddみたいなキーワードだと違うものまで引っかかりそうだったので