本記事は「ポインタ/参照とconst
キーワードについて、なんとなく分かってきたかも?」という学習ステージの方が、const修飾の役割を イメージできる ような理解を目指しています。
図解(凡例)
int val = 123; // 普通の変数
int* ptr = &val; // ポインタ(pointer)
int& ref = val; // 参照(reference)
const int cval = 45; // 定数(constant)
本記事では、上記C++ソースコードに対応する図示を次のルールで行います。"白色の箱"が普通の変数(cval
)やポインタ型変数(ptr
)を、"赤色の箱"が定数(cval
)を、"黄色タグ"が参照型変数(ref
)をそれぞれ表します。箱の中身は、各変数が保持している値に対応します。ポインタ型変数は"差し先変数のアドレス値"を保持しますが、分かりやすさのため矢印(→)にて表現します。参照型変数についても同様です。1
const × ポインタ型
ポインタ型変数の宣言では、アスタリスク(*
)の前または後ろにconst
キーワードを配置できます。つまりconst
の有無に応じて4種類のポインタ型変数が存在します。なお先頭アスタリスク(*
)よりも前では、const int
/int const
いずれの書き方でも同じポインタ型となります。お好みの順序で利用ください。
int * ptr1 = &val;
const int * ptr2 = &val;
int const * ptr2 = &val; // 上に同じ
int * const ptr3 = &val;
const int * const ptr4 = &val;
int const * const ptr4 = &val; // 上に同じ
constポインタの図解
それぞれのポインタ型と、const修飾の関係を図示します:
- アスタリスク(
*
)の前にconst
を置いた場合、ポインタ型変数ptrN
を介して差し先の値42
を書き換えることはできません。(図中では"赤枠"で表現) - アスタリスク(
*
)の後ろにconst
を置いた場合、ポインタptrN
は定数となります。つまり差し先の変数/矢印が向かう先は固定です。
前者では、あくまで “このポインタを経由した” 値の書き換えが禁止されるだけであり、このポインタ経由でなければ値は書き換え可能です。
constポインタと定数の関係
先ほどはポインタの指す先が普通の変数でしたが、ポインタの指す先が定数(const int
)の場合は次の関係になります;
int * ptr5 = &cval; // NG: コンパイルエラー!!
const int * ptr6 = &cval; // OK
int * const ptr7 = &cval; // NG: コンパイルエラー!!
const int * const ptr8 = &cval; // OK
const × 参照型
参照型変数の宣言では、アンパサンド(&
)の前にのみconst
キーワードを配置できます。つまりconst
の有無に応じて2種類の参照型変数が存在します。ポインタ型と同様にconst int
/int const
は順不同です。2
int & ref1 = val;
const int & cref1 = val;
int const & cref1 = val; // 上に同じ
const参照の図解
それぞれの参照型と、const修飾の関係を図示します:
図示では省略しましたが、アンパサンド(&
)の後ろにconst
キーワードを書くことはできません。仮に書けた場合は「参照型変数の参照先は固定」に相当しますが、もともと参照型変数の参照先を変更することは不可能ですから、コンパイルエラーとして扱われます。
int & const ref = val; // NG: コンパイルエラー
// ^^^^^
const int & const cref = val; // NG: コンパイルエラー
// ^^^^^
const参照と定数の関係
参照の指す先が定数(const int
)の場合は次の関係になります;
int & cref1 = cval; // NG: コンパイルエラー
const int & cref2 = cval; // OK
おしまい。
-
参照型変数だけが"箱"形をしていないことに気づいたでしょうか?C++言語では、参照型変数と通常変数・ポインタ・定数が明確に区別されることを反映しています。通常変数・ポインタ・定数はメモリ上に実体が存在するため、本文中では"箱"のイメージを用いました。一方の参照型変数はその形状が示す通り、別の実体に対するタグ(tag)/別名(alias)と解釈できます。(厳密な議論はこちらの記事を参照) ↩
-
古き良きC++03時代には、ある型
T
に対する参照型は1種類(T &
)しか存在しませんでした。C++11以降のモダンなC++言語では、従来からの左辺値参照型(T &
)と新しい右辺値参照型(T &&
)とで2種類の参照型が存在します。const
キーワードも考慮すると、厳密には4種類の参照型(T &
,const T &
,T &&
,const T &&
)となるのです。ただし、本記事範囲では右辺値参照型(T &&
)へ言及する必要性がないため、昔ながらの左辺値参照型(T &
)のみを対象としています。 ↩