C/C++で、ポインタや参照が絡んだ時に const をどこにつければいいか一瞬迷うことがある。そのため、 const の考え方について自分用にメモする。
constの考え方#
覚えておくべきルールは以下の2つだけである。
- 冒頭の
constは直後の型名と入れ替えても同じ意味const intとint constは同じ型const int*とint const*は同じ型だがint* constとは違う型
const、volatile、*、&、&&は直前の型を修飾して別の型にする記号volatile int* const&は(((volatile int)*) const)&のイメージ。日本語で書き下すなら、((書き換え不可能な((volatileなint)を指すポインタ))への参照)となる。
constをつける位置に迷ったら、まず1の考え方に従い<型名> const の順に入れ替える。通常のコーディングではconstを手前に置く方が主流だが、constの位置に迷った時は1.のルールで脳内変換した方が考えやすい。
例題#
例題:T=const char* のとき、const T& を現す型は何か?
解答#
それぞれに1.を適用すると以下のようになる。
T=const char*->T=char const*const T&->T const&
T=char const* を const T& に代入すれば、求める型は char const* const&(=const char* const&)だと分かる。「constは直前の型を修飾する」という大原則を忘れなければ、constを付ける位置に迷うことは少なくなるはずだ。
constexprの考え方#
さて、constの考え方は上に述べた通りだが、constexprはルールが異なるので注意が必要である。constexprは、宣言する変数がROM化可能(コンパイル時に計算可能)であることを修飾するキーワードである。すなわち、constexprは型ではなく変数にかかるイメージである。
例えば、const char* x は「const charを指すポインタ変数 x」を表すのに対し、constexpr char* xは「charを指すポインタ変数xはROM化可能」を表す。
コードで差分を示すと以下のようになる。
int g = 334;
// OK: グローバル変数 g への書き換え不可能なポインタ
const int* a = &g;
// OK: グローバル変数 g へのポインタ(グローバル変数のアドレスはROM化可能)
constexpr int* b = &g;
int main() {
const int l = 264;
// OK: a の書き換えは禁止されていない
a = &l;
// NG: b の書き換えは禁止
// b = &l;
// NG: a は const int を指すので書き換え不可
// *a = l;
// OK: b の中身は書き換え可能
*b = l;
}
とてもややこしいので注意が必要だ1。
constexpr と const の関係については以下の記事も参照。
constexprでもconstの1.のルールは使える。すなわち、constexpr int xはint constexpr xと書くこともできる ↩︎
