読者です 読者をやめる 読者になる 読者になる

C# で 劣化 Variant を書いてみた (2)

.NET

id:NyaRuRu:20080519:p1 にちょっとだけ続き.

Generative Programming より

多重定義の機能は、多くの静的な型づけ言語が備えています (例えば、CとPascalは多重定義をサポートしませんが、C++Javaはサポートします)。多重定義はすべての引数の型を使って異なる実装を区別しますが、戻り値の型は使わないことに注意して下さい。通常、多重定義では (オーバーライドにおいても) 戻り値の型についてはサポートしません。なぜなら、関数は「単なる副作用」としても呼び出せるからです。例えば、

foo();
これは、次の代用です。
ResultType temp = foo();

戻り値の型だけが異なる実装が複数用意されていたとき、前者の呼び出しではコンパイラ (もしくはランタイムシステム) はどの実装を呼び出すべきなのかが判断できません。

まあそういう意見はあるよなぁと思いつつ,「導入できない理由」と「導入しない理由」ってのは違うよなぁという気も.曖昧なときはコンパイルエラーという場面は他にもあちこちありますし.(といいつつ別に C++ や C# に欲しいというわけじゃないんですが)
んでは次のはいかが?

template なキャスト演算子と左辺から右辺の推論

GetProcAddressで戻り値をキャストするのが面倒くさいので少し考えてみた

こんな感じでどうでしょう。

struct ProcAddress {
  ProcAddress(HMODULE module, LPCSTR name) : p_(0) {
    p_ = ::GetProcAddress(module, name);
  }
  template<class T> operator T() const { return reinterpret_cast<T>(p_); }
  FARPROC p_;
};

こうやって使います。

HMODULE module = 0;
typedef void (__stdcall *Func)();
Func f = ProcAddress(module, "test");

これの特殊版が前回の『C# で 劣化 Variant を書いてみた - NyaRuRuの日記』ですな.
ようするに implicit な型変換が登場する場面では,逆方向の推論っぽく見えるという感じでしょうか.C# の method groups から delegate への implicit conversion もこのタイプですな.

プログラミング言語 merd

k.inaba さんの紹介記事 が参考になります.とてもかっちょいいです.

ad-hoc overloading

上のように関数の引数として渡せると言うことは、 単純な「名前が同じに宣言された関数を、多重に扱う」という処理ではなく、「複数の実体がオーバーロードされている値」 という特殊な値を言語的に扱っている、ということを意味しています。

つまり、「この値とこの値を多重に重ねたい!」と思ったら、 その場でコードとして書くことができます。例を見てみましょう。 |&|演算子を使います。

x = 1 |&| "one"

x.println
(x + 3).to_string.println
"one"
4.

使われている場所に応じて数の 1 だったり文字列 "one" として扱われる値、x を定義しています。

こういうのを見ていると,「こんなにも・・!! こんなにも苦しいのならば型などいらぬ!!」っていわれるほど静的型は嫌われなくてよいのになぁ,とか思ったり.