C++のconstと格闘中

ちょっとC++を使う機会があったのでどうせならと色々試しています。
今回初めて触ったのはshared_ptrとconst。
shared_ptrはvectorに突っ込むときにコピーコンストラクタ呼びたくないなーというだけの理由なんですが、他に思いつかなかったのでこれで。いろんな宗教に影響されてか生ポインタを毛嫌いしていたので参照を使い回したら、謎の挙動を見せてくれたり、これはこれで難航しました。
で、const。

void save(const char *const file) const;
  • 一つ目は*にかかっていて、参照先を変更してはいけない
  • 二つ目は仮引数にかかっていて、その値を変更してはいけない
  • 三つ目はメソッドにかかっていて、このメソッドはオブジェクトの状態を変化させませんよということ

書き始めたときは違うとらえ方をしていて、

  • 一つ目はcharにかかっていて、変更してはいけない値ということ
  • 二つ目は*にかかっていて、変更してはいけないポインタということ

と書いていたのだけど、後半書いてて違和感が。値にconstかどうかって属性はないような。

  • 代入、実引数、戻り値はコピーを"もらう"
  • 所有しているわけなので、自分のものはどうしようと勝手だろ
  • ポインタや参照ならその先は実体の所有者に従うべき

というニュアンスなのかな?

多すぎて理解できません

文に変形させて読み解くのはK&R本で読んだ気がするんだよな−。と思ってメモをあさったら何か出てきた。

char (*(*x()))()
(char (*)()) *x()
((((char Function()) Pointer) Array) Pointer) x()
x()の返すポインタ<配列内のポインタ

これはひどい。現実にはここまで深いのはtypedefしてると信じることにしよう。

宣言と定義

int size(void) const;
int size(void) const{...}

メソッドに付くconstの有無は大きな問題らしいので、当然合わせる。
参考 C++のconstは別のメソッドを作る - 脳味噌出力中

void load(const char * file);
void load(const char * const file){...}

値渡し=コピーにはなんら制限がかからないから、宣言にconst付ける意味がないと言えばそうだな。

void load(char * file);
void load(const char * const file){...}

これはコンパイルできない。先の流れから二つ目はどうでもいいけど、一つ目は型の一部だから勝手に変えられない、ということか。
似て非なる例で、

char *a;
const char * const b = a;

この場合constが付いたところで制限が厳しくなるだけなので、暗黙のキャストが働く。
だから仮引数でも通るかと思ったのだけど、さすがに無理か。*1

まとめ

  • ポインタにconstを付けると、参照先を変更してはいけないという意味を持つ
    • 実体を所有しているわけじゃないので、制限が緩くなる方向へのキャストが制限される
      • const_castで回避可能
  • 変数・仮引数にconstを付けると、その変数・仮引数の値を変更してはいけないという意味を持つ
    • constのついていない変数にコピーするのは可
    • 仮引数の場合は宣言と定義が異なっていても可
    • 参照変数を初期化するときはポインタ同様にキャストが制限される(つまりconst付けるのは可)
  • メソッドにconstを付けると、オブジェクトの状態を変化させないメソッドという意味を持つ
    • フィールドの変更や非constメソッドの呼び出しが制限される

という理解をしました。あってるといいな。

*1:冒頭にあった「値がconst」という考え方だと、file→const fileはキャストが働いているのだと思ってた