プログラミング言語の重箱の隅をつつくシリーズ(って、そんなシリーズねーよ)。
※ひとつ前が、あんまり僕らしくもない(と僕自身が感じる)エントリーだったのでコレ追加しよう、っと。
少し前に、無値(値がないということ)を話題にしました。その後、C/C++のvoidってのは、単に無値を意味するだけではないことに気付いたので書いておきます。
まず、voidの解釈として、値の集合が空集合と、値の集合が単元集合(singleton set)がありますが、空集合はちょっと変で単元集合が良かろうって言いました。それで、voidは単元集合(ただし、そのメンバーが何であるかは重要ではない)だと理解します。
さて、void *p;
って書いてあるとき、pが指す先に“voidの値”があるってわけではありません。なにか未知のモノがあるという意味です。気分としては、Any *p;
です。pが指す先に置いてあるデータの可能性は、巨大な集合を形成します。
Anyの集合(普遍集合とか全体集合と言えばいいのかな)は、空集合や単元集合の対局にあるものです。なのにどっちも記号「void」で示す、ってのは納得できない -- で、少しコジツケ考えました。
実はvoidの意味はAnyだったとします。void foo();
は、fooの戻り値が何にでもなり得ることです。そんなバカな -- いやいや、そうでもない。戻り値はレジスタかスタックに入るとして、関数fooはそれらの戻り値ストレージをいじりません。となると、戻り値ストレージにはfoo呼び出し前の値が残っていることになります。ひょっとして、fooのなかで戻り値ストレージを破壊する可能性も否定できません。いずれにしても、戻り値ストレージの状態は不明。
fooの呼び出し後に無理に戻り値ストレージから値を取り出すとしても、それが何であるかはまったく不明、何も保証できない。これは、void *p;
なpの先にアクセスするのと事情が似ているのでは。
しかし、Javaのような言語で、void foo();
とObject foo();
が似たようなものか?と詰問されると困ってしまいます。やっぱダメか。
少しだけ教訓的なことを(無理に)言えば; Never(空集合)、Void(単元集合)、Any(普遍集合)などは特殊なものですが、こういう特殊性の分析はおざなりになりがちで、取り違えられたりすることが多いってことです。ですから、特殊な状況にこだわってみると、意外と本質が見えたりすることもあります。
圏論的に言えば、VoidとAnyは実際似てます。Voidは集合圏の終対象、Anyはベキ集合Pow(X)をinclusionで圏と見なしての終対象です。そう、終対象つながり(余計にコジツケになるかも)。