【C++】constと多重ポインタの組み合わせで発生する代入エラー
はじめに
独習C++の「1.5.4 const修飾子とポインター」のColumnにconstと多重ポインタの組み合わせで発生する代入エラーについて記載がありましたが、すぐに理解できなかったため自分なりに整理しました。
本題
エラーの内容
エラーの内容としては、以下のようなものです。
int val = 123;
int* ptr = &val;
const int** pptr = &ptr; // エラーが発生する
ポインタのポインタがconstとして定義されている場合、
ptr
の型はint*
のため、&ptr
の型は(const int**
ではなく)int**
になります。
そのため、const int**
であるpptr
にはそもそも&ptr
は代入できません。
本来ならこれで話が終わりますが、厄介な例外があります。
例外
厄介な例外とは、非const変数へのアドレスをconstポインターへ代入できる(int*
をconst int*
へ代入できる)というものです。
具体的には以下のようなものです。
int val = 7;
const int* ptr = &a; // エラーなく代入できる
これは、constポインターの方がより条件が厳しくなるためであり、言い方を変えるとconstポインターにすることでできることが増えないからです。
エラーが発生する理由
それでは本題に話を戻します。先ほどのコードを再掲します。
int val = 123;
int* ptr = &val;
const int** pptr = &ptr; // エラーが発生する
先ほどは「pptrと&ptrは型が違うから代入できない」と説明しましたが、例外での説明を勘案するとint**
をconst int**
へ代入できてもよさそうな気がします。
結論としては、型の安全性の観点から明確に危険な操作ができてしまうため、C++はそれをコンパイルエラーとして防いでいます。
危険な操作とは、本来constとして定義されているはずの変数を、ポインタからの間接参照で変更するというものです。
具体的にどういうことかというと、pptr
に入っているptr
の型が(const
がついていない)int*
だと、*ptr=...
のような間接参照の形でval
を変更できてしまいます。
つまり、const int** pptr
で定義しているはずのconst
が破られてしまっているということになります。
上記のような事象を防ぎ型安全性を確保するため、int**
をconst int**
へ代入することがコンパイルによって禁止されています。
エラーを回避するには
以下のようにすればptr
からの間接参照でもval
が変更される危険性がないため、コンパイルが通るようになります。
int val = 123;
const int* ptr = &val;
const int** pptr = &ptr; // エラーが発生する
補足
上記の説明では「ポインタのポインタ側のconst
を突破できる」という説明になっていますが、参考文献では「元の値で定義しているconst
を突破できる」という内容になっています。参考文献の内容を読まれる際はご注意ください。
参考文献
Discussion