📖

【C++】constと多重ポインタの組み合わせで発生する代入エラー

に公開

はじめに

独習C++の「1.5.4 const修飾子とポインター」のColumnにconstと多重ポインタの組み合わせで発生する代入エラーについて記載がありましたが、すぐに理解できなかったため自分なりに整理しました。

https://www.shoeisha.co.jp/book/detail/9784798150239

本題

エラーの内容

エラーの内容としては、以下のようなものです。

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を突破できる」という内容になっています。参考文献の内容を読まれる際はご注意ください。

参考文献

https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion

Discussion