はじめに
タイトルに大意はありません.字面がよかったから付けただけです.内容共々,他の記事と被ってたらすみません.
「C# が嫌いで見たくもない」というわけではありませんし,まぁ悪くない言語だと思っています(特にGUIアプリケーション作るなら).
なにより,Mono と OmniSharp には大変お世話になりました.以上,言い訳タイムでした.
この記事は, 自分が出くわした問題を,よく訓練された C#er の方々はどう解決しているのか ということを知りたい,という目的のもと書いています.
そして,最終的に知りたいことは, C# ではどうやって 論理的const を実現するのか です.
const メンバ関数ないのん?
ちょっとした興味から C# を触っていたある日,「そういや例のお茶みたいな名前の言語には C++ みたいな const メンバ関数なかったよな」と思い,C# はどうなのか調べたら… どうやらないらしい.
え? ないの? なんで?
C++
の ++
なんだよね? なんでないの?
教えて,Google先生!
…いろいろと調べた結果,一番しっくりきた説明が
おそらく const メンバ関数が存在することによる言語としての複雑さの拡大を嫌ったのだろう
でした.(正否は問わず,理解出来るという意味で)
他にも,
- 戻り値に const なり readonly なりつけれるけど,それじゃダメなの?
- そもそも const メンバ関数の必要性を感じない
- そもそも const メンバ関数って何?
という意見もあったけど,「戻り値に〜」は問題点が違う気がする.
必要性に関しては,そもそも C++ だったら const 参照とか経由すると const メンバ関数しか呼べないし,「自分のなかでは存在するのが当たり前だったので,ないとなんか"いずい"」としか言えない.
プログラマとは怠けるために全力を出す生き物である
で,話を戻すと,「複雑さの拡大を嫌った」というのはなんとなく理解できる.
例のお茶言語 を「考えたくない人向け言語」と呼んだ人がいるらしいが,要は 裏で自動的にできるものはプログラマが考えなくていいようにしよう という考え方は理にかなっているように思う.
この点に関しては GC なんかがいい例だし,C# はその点で必要なら構造体使って GCレス なコードも書けるみたいで,好感が持てる.(そこまでするなら C++ 使えよって話だが)
でもどうだ,メンバ関数の const 性はプログラマが考えなくていいことだろうか?
普通に使う分には,const ref
経由でオブジェクトにアクセスしていれば,オブジェクトの const 性は保たれるはずなので,それでいい気もするし,わざわざ const でないオブジェクトにはアクセスできないメンバ関数があるというのも,インタフェース的に複雑な気もする.(後述の通り個人的には気に入っている点ではあるのだけれども…)
ポインタの使用が当たり前の C++ ならともかく,ポインタが一応非推奨の C# ならわからなくもない.
だがしかし,それで保たれるのは "bitwise const" ではないか…?
我々が求めているのは "logical const" であって,const メンバ関数なら mutable
と組み合わせてそれが実現できるし,なによりわかりやすいではないか.(個人的感想を多分に含みます)
一番の厄介者であるバグを減らせるのであれば,多少の面倒は受け入れてもお釣りがくるのではないか?
オブジェクトの const 性
そもそも,ここでいう const 性は以下の2つを扱う.
type | description |
---|---|
bitwise | オブジェクトがビット単位で物理的に const |
logical | オブジェクトの物理的内部状態は関係なく,メンバ関数の入出力(オブジェクトの外からの見た目)について const |
それぞれの説明はテキトーなので,わからない人,気になる人は Google 先生に聞くといいと思います.
Bing? 知らんなそんな奴は.
当然 mutable もない(たぶん)
C++ で "logical const" を実現する方法として,mutable
指定子(正式名忘却)が存在する.
mutable
がついたメンバ変数は,const メンバ関数内でも変更可能になる.
これの何がありがたいって,const なオブジェクトであっても,内部でキャッシュを持てるのだ.
オブジェクト指向を嗜む皆さんなら単純に内部状態を返すだけのアクセサは可能な限り排除するよう訓練されていると思うが,オブジェクトの内部状態が変わってないのに何度も戻り値を計算しなおすのがバカらしくなったことはないだろうか.
そういうときこそ,mutable
でキャッシュとフラグを持って,計算済みならそれを返すということができる.
では,これを C# でやってみよう…
ほうほう,const メンバ関数はないと… てことは mutable
指定子も存在する意味ないよな…?(調べた限り見当たらなかった)
で,const ref
経由でオブジェクトにアクセスした時に内部状態を変更すると怒られると…(そりゃ怒るよね)
はて? どうしろっちゅうねん…
とにかく,const メンバ関数が欲しい
mutable
はほんの一例で,こんなこともできるよ〜程度(実際,有効な場面はけっこう限定的)なのだが,個人的に const メンバ関数の気に入っている点は,const なオブジェクトからは const メンバ関数しか呼べない,というところだ.
コーディングしていて,こんなにも役にたつ宣言が他にあるだろうか.
関数自身が,「俺はオブジェクト変更する(かもしれない)ぜ!」とか「私は(表向きには)変更しないから,気にせず呼んでちょうだい」とか宣言してくれているのだ.
一つの関数内など,全体的な処理単位で 非const なオブジェクトがあっても,const メンバ関数の呼び出し前後では当該オブジェクトが const であると仮定できて,そこに関しては何も(複数スレッドからの同時アクセスなどを除く)心配する必要はないのだ.
これ,捗りますよね?… ね?
ね?
さいごに
ふと思い立っていろいろ書きましたが,実は C# 使ってたの結構前だし,復習がてら使ってみたのも半年以上前だし,teratail や Qiita 常駐組のようなダークサイドまではいかないまでも一応 C++er なので,正直あまり C# 詳しくないです.
なので,「今は違うよ」とか「こんな機能があるよ」とかあれば教えて欲しくかったのと,ちょっとした現実逃避がしたかったので,この記事を書いたというのが本音です.
もしかしたらキャッシュなんかは自動でやってくれるのかもしれない(JITできるなら可能性もなくはないだろう)し,自分も C++ で初めて const メンバ関数を見たときは「なにこれ?何に使うん?」て思ったので,裏でできることと学習コストを考えたら「なくていいや」という結論なのかもしれません.
でもやっぱ "いずい". ← const おじさんだからね,仕方ないね
あとなんだかんだ,C++ のめんどくさいけど頼めばなんでもやってくれるツンデレ感がたまら(ry