C++の定数とは、基本的には一度決めた値を変更できないようにする仕組みです。
ただし、C++では「定数」と呼ばれるものが1種類だけではありません。
代表的なものには、const、constexpr、consteval、constinit、enum class などがあります。
それぞれ役割が少しずつ違います。
constは「変更できない」ことを表します。constexprは「コンパイル時に評価できる」ことを表します。constevalは「必ずコンパイル時に評価する」ことを表します。constinitは「静的初期化されることを保証する」ための仕組みです。enum classは「型安全な定数の集合」を表すときに使います。
つまり、C++で定数を理解するには、単に「値を変えられない変数」と考えるだけでなく、いつ値が決まるのか、どこから変更できないのか、どのような目的で使うのかを意識する必要があります。
constの基本
constは変更不可を表す
const は、C++で最も基本的な定数の指定です。
変数に const を付けると、その変数は初期化後に変更できなくなります。
たとえば、最大人数、画面サイズ、税率、判定基準など、プログラム中で意味のある固定値を表すときに使います。
直接数字を書くよりも、名前付きの定数にした方がコードの意図が分かりやすくなります。
たとえば「80」という数字だけが書かれていると、それが合格点なのか、年齢制限なのか、最大回数なのか分かりません。
しかし「合格点」という名前の定数にしておけば、その値の意味が明確になります。
このように const は、値を変更できないようにするだけでなく、コードの可読性を高める目的でもよく使われます。
constは「その名前から変更できない」という意味
const を理解するときに重要なのは、const が常に「プログラム全体で絶対に値が変わらない」という意味ではないことです。
多くの場合、const はその変数名、参照、ポインタを通じて変更できないという意味になります。
たとえば、ある通常の変数を const 参照で受け取った場合、その参照からは値を変更できません。
しかし、元の変数そのものが非 const であれば、元の変数から値を変更することはできます。
つまり const は、完全な不変性というよりも、読み取り専用の見え方を作る仕組みとして働くことがあります。
この考え方は、ポインタ、参照、関数引数、メンバ関数を理解するときに非常に重要です。
ポインタとconst
ポインタではconstの位置が重要
C++で const が難しく感じられる大きな理由の1つが、ポインタとの組み合わせです。
ポインタの場合、const がどこに付くかによって意味が変わります。
大きく分けると、次の3種類があります。
| 種類 | 意味 |
|---|---|
| 指す先が変更不可 | ポインタ経由で値を変更できない |
| ポインタ自体が変更不可 | 別の場所を指せない |
| 指す先もポインタ自体も変更不可 | 値も向き先も変更できない |
特に注意したいのは、「指す先が変更不可」という表現です。
厳密には、これはそのポインタ経由では指す先を変更できないという意味です。
元の変数が本当に const でなければ、別の経路から変更できる場合があります。
指す先が変更不可の場合
ポインタが指している値を変更できない形があります。
この場合、ポインタ自体は別の場所を指すことができます。
しかし、そのポインタを通じて値を書き換えることはできません。
これは、関数にデータを渡すときに「この関数内では中身を変更しません」と示したい場合によく使われます。
「読み取り専用のポインタ」と考えると分かりやすいです。
ポインタ自体が変更不可の場合
一方で、ポインタ自体を変更できない形もあります。
この場合、ポインタが指している先は固定されます。
つまり、一度ある変数を指したら、別の変数を指すようには変更できません。
ただし、指している先の値が非 const であれば、その値自体は変更できます。
つまり、この場合に固定されているのは「ポインタの向き先」であって、「指している値」ではありません。
指す先もポインタ自体も変更不可の場合
さらに、指す先もポインタ自体も変更できない形があります。
この場合、ポインタは別の場所を指せません。
また、そのポインタを通じて指す先の値を変更することもできません。
つまり、ポインタとしても読み取り専用であり、向き先も固定されています。
読み方のコツ
ポインタと const を読むときは、const が何を修飾しているのかを確認することが大切です。
慣れるまでは、次のように考えるとよいです。
const が型の側にある場合は、指す先の値を変更できない。
const がポインタそのものの側にある場合は、ポインタ自体を変更できない。
最初は混乱しやすいですが、実務では頻繁に出てくるため、早めに慣れておくとよいです。
参照とconst
const参照は読み取り専用の参照
参照にも const を付けることができます。
const 参照は、参照先の値をその参照経由で変更できないようにします。
ただし、ここでも重要なのは「参照先そのものが必ず完全に不変になるわけではない」という点です。
元の変数が非 const であれば、元の変数からは変更できる場合があります。
しかし、const 参照を通じては変更できません。
関数引数でよく使われる
const 参照は、関数引数で非常によく使われます。
特に、文字列、配列風のオブジェクト、構造体、クラスのインスタンスなど、コピーするとコストがかかるものを関数に渡すときに便利です。
値渡しにするとオブジェクトがコピーされます。
一方、参照渡しにすればコピーを避けられます。
ただし、通常の参照渡しでは関数内で値を変更できてしまいます。
そこで const 参照を使うことで、コピーを避けながら、関数内で変更しないことも保証できます。
つまり、const T& は次の2つを同時に満たすための書き方です。
| 目的 | 内容 |
|---|---|
| 効率 | コピーを避ける |
| 安全性 | 関数内で変更しない |
C++では非常に重要な書き方です。
関数引数とconst
値渡し引数のconst
関数引数が値渡しの場合、その引数は呼び出し元の値のコピーです。
そのため、関数内で引数を書き換えても、呼び出し元の値には影響しません。
値渡し引数に const を付けると、関数内でそのコピーを変更できなくなります。
ただし、これは主に関数の実装者向けの意味です。
呼び出し側から見ると、値渡し引数が const かどうかはあまり重要ではありません。
そのため、公開する関数宣言では、値渡し引数に const を付けないことも多いです。
一方、関数の定義側で「この引数は関数内で変更しない」という意図を示すために const を付けることはあります。
参照渡し引数のconst
参照渡しの場合、const の意味はより重要です。
通常の参照渡しでは、関数内で引数の中身を変更できます。
しかし、const 参照にすると、関数内で変更できなくなります。
そのため、関数が値を読むだけなら、可能な限り const 参照を使うのが自然です。
特に、サイズの大きいオブジェクトやコピーコストの高いオブジェクトでは、const 参照がよく使われます。
小さな型では値渡しでもよい
一方、整数、浮動小数点数、文字、真偽値などの小さな型では、無理に const 参照にする必要はありません。
小さな型はコピーが軽いため、値渡しの方が単純で分かりやすいことが多いです。
たとえば、整数を1つ受け取るだけなら、値渡しで十分です。
大まかな使い分けとしては、次のように考えるとよいです。
| 対象 | よく使う渡し方 |
|---|---|
| 整数や真偽値など小さな型 | 値渡し |
| 文字列や大きなオブジェクト | const 参照 |
| 関数内で変更したい値 | 非 const 参照またはポインタ |
| 所有権を移したい値 | ムーブを考慮 |
戻り値とconst
値で返す戻り値のconstは基本的に不要
関数の戻り値に const を付けることもできます。
しかし、値で返す戻り値に const を付けることは、現代C++では基本的に不要です。
特に、整数や浮動小数点数などの基本型を値で返す場合、戻り値に const を付けても実用上の意味はほとんどありません。
また、クラス型を値で返す場合に const を付けると、ムーブや代入などの使い勝手に悪影響が出ることがあります。
そのため、値で返す関数では、基本的に戻り値に const を付けない方が自然です。
参照を返す場合のconstは意味がある
一方で、参照を返す場合は const に意味があります。
オブジェクト内部のメンバを参照で返す場合、非 const 参照を返すと、呼び出し側から内部状態を書き換えられてしまいます。
それを防ぎたい場合は、const 参照を返します。
これにより、呼び出し側は値を読むことはできますが、戻り値を通じて内部状態を変更することはできません。
クラスのgetterでは、この形がよく使われます。
メンバ関数のconst
末尾のconstはオブジェクトを変更しないことを表す
C++のクラスでは、メンバ関数の末尾に const を付けることがあります。
これは、そのメンバ関数がオブジェクトの状態を変更しないことを表します。
通常、値を取得するだけの関数、状態を確認するだけの関数、サイズや名前を返すだけの関数などには、末尾に const を付けます。
たとえば、次のような関数には const を付けるのが自然です。
| 関数の種類 | constを付けるべきか |
|---|---|
| 値を取得するだけ | 付ける |
| 状態を確認するだけ | 付ける |
| 内部データを書き換える | 付けない |
| カウンタを増やす | 通常は付けない |
| 設定値を変更する | 付けない |
constオブジェクトから呼べる関数
const オブジェクトからは、基本的に const メンバ関数しか呼べません。
これはとても重要です。
たとえば、ある関数がオブジェクトを const 参照で受け取った場合、その関数内では const メンバ関数しか呼べません。
そのため、状態を変更しないメンバ関数に const を付け忘れると、読み取り専用の場面で使いにくいクラスになってしまいます。
特にgetterには、基本的に const を付ける習慣を持つとよいです。
論理的な不変性
メンバ関数の const は、厳密には「ビット単位で絶対に何も変えない」という意味ではなく、一般的には外部から見たオブジェクトの意味ある状態を変えないという意味で使われます。
たとえば、キャッシュ、ログ、デバッグ用カウンタなどは、外部から見た本質的な状態ではない場合があります。
このような場合に mutable が使われることがあります。
mutable
mutableはconstメンバ関数内でも変更できるメンバを表す
mutable は、const メンバ関数の中でも変更できるメンバ変数を表すために使います。
通常、const メンバ関数の中ではメンバ変数を変更できません。
しかし、mutable が付いたメンバ変数だけは例外的に変更できます。
主な用途は次のようなものです。
| 用途 | 内容 |
|---|---|
| キャッシュ | 初回だけ計算して保存する |
| 遅延評価 | 必要になったときに値を作る |
| ログ | 呼び出し回数などを記録する |
| デバッグ情報 | 内部的な観測情報を保持する |
ただし、mutable は乱用しない方がよいです。
本来変更してはいけない状態まで mutable にしてしまうと、const の意味が弱くなり、コードの安全性が下がります。
constexpr
constexprはコンパイル時に評価できることを表す
constexpr は、コンパイル時に値を評価できることを表します。
const が「変更不可」を表すのに対して、constexpr は「コンパイル時に計算可能」を表します。
この2つは似ていますが、目的が違います。
| キーワード | 主な意味 |
|---|---|
const | 変更できない |
constexpr | コンパイル時に評価できる |
たとえば、配列サイズ、テンプレート引数、コンパイル時に決めたい設定値などでは、constexpr が向いています。
constでもコンパイル時定数になる場合がある
const で宣言された値も、条件によってはコンパイル時定数として扱われることがあります。
たとえば、整数型の const 変数がコンパイル時に値を決められる形で初期化されていれば、配列サイズなどに使える場合があります。
しかし、const はあくまで「変更不可」が主目的です。
実行時に入力された値で初期化された const 変数は、変更できませんが、コンパイル時定数ではありません。
この違いは重要です。
値がコンパイル時に決まることを明確にしたい場合は、constexpr を使う方が分かりやすく、安全です。
constexpr変数は基本的に変更不可
constexpr 変数は、基本的にその変数自体が変更不可になります。
そのため、constexpr はコンパイル時に評価できるだけでなく、定数としても扱われます。
ただし、ポインタの場合は注意が必要です。
constexpr がポインタに付いた場合、変更できなくなるのは主にポインタ自体です。
指している先の値まで自動的に const になるとは限りません。
つまり、constexpr ポインタは「ポインタ自体が固定される」と考える必要があります。
constexpr関数
constexpr関数はコンパイル時にも実行時にも使える
constexpr は変数だけでなく、関数にも使えます。
constexpr 関数は、条件がそろえばコンパイル時に評価できます。
一方で、実行時の値を渡した場合は、通常の関数のように実行時に評価されます。
つまり、constexpr 関数は「必ずコンパイル時に実行される関数」ではありません。
正確には、コンパイル時に評価できる場面ではコンパイル時に評価できる関数です。
この点は consteval との違いとして重要です。
コンパイル時定数を作るときに便利
constexpr 関数は、コンパイル時に計算した値を定数として使いたい場合に便利です。
たとえば、単純な計算、変換、固定設定の導出などをコンパイル時に行いたい場合に使われます。
現代C++では、constexpr にできる関数の範囲が広がっており、定数式プログラミングの重要な機能になっています。
consteval
constevalは必ずコンパイル時に評価される
consteval は C++20 で導入された機能です。
constexpr 関数は、コンパイル時にも実行時にも使えます。
それに対して、consteval 関数は必ずコンパイル時に評価されます。
そのため、実行時にしか分からない値を渡して呼び出すことはできません。
この意味で、consteval は constexpr よりも強い指定です。
| キーワード | 関数としての意味 |
|---|---|
constexpr | コンパイル時に評価できる場合がある |
consteval | 必ずコンパイル時に評価される |
constevalを使う場面
consteval は、コンパイル時に必ず値を決めたい処理に使います。
たとえば、コンパイル時に検証したい値、コンパイル時に生成したいID、コンパイル時に固定したい設定などで使われることがあります。
ただし、通常のアプリケーション開発では、まず constexpr を理解してから consteval を学ぶ方が自然です。
constinit
constinitは変更不可を表すものではない
constinit は C++20 で導入された機能です。
名前に const が含まれていますが、constinit は「変更不可」を表すものではありません。
constinit は、静的記憶域期間またはスレッド記憶域期間を持つ変数が、静的初期化されることを保証するための指定です。
つまり、constinit の目的は値を変えられなくすることではなく、初期化タイミングを安全にすることです。
通常のローカル変数には使えない
constinit は、通常のローカル変数には使えません。
基本的には、グローバル変数、名前空間スコープの変数、静的メンバ変数、関数内の静的ローカル変数、スレッドローカル変数などに使います。
通常の関数内ローカル変数に使うものではない点に注意が必要です。
constと組み合わせることもできる
constinit は変更不可を意味しませんが、const と組み合わせることはできます。
その場合は、静的初期化されることを保証しつつ、値も変更不可になります。
つまり、constinit 単体では変更可能な変数にも使えますが、const と組み合わせれば読み取り専用にできます。
#defineによる定数
C++では基本的に避ける
C言語由来の方法として、マクロを使って定数を定義する方法があります。
しかし、C++では定数目的のマクロは基本的に避けた方がよいです。
理由は、マクロには型がなく、スコープもなく、デバッグもしにくいからです。
C++では、型安全でスコープを持つ const や constexpr を使う方が自然です。
マクロよりconstexprを優先する
固定値を定義したい場合、現代C++では基本的に constexpr を優先します。
特に、数値定数、文字列定数、配列サイズ、設定値などは、マクロではなく constexpr で表す方が安全です。
マクロが必要になる場面もありますが、単純な定数定義に使う必要はほとんどありません。
enumとenum class
定数の集合には列挙型を使う
状態、種類、モード、結果など、関連する定数の集合を表したい場合は、列挙型を使います。
たとえば、色、状態、エラー種別、処理結果、画面モードなどです。
このような値を単なる整数定数で表すと、意味の違う値を混同しやすくなります。
列挙型を使うことで、値の意味が明確になります。
現代C++ではenum classが推奨される
C++では、従来の enum よりも enum class が推奨されます。
enum class は、列挙子の名前が外側のスコープに漏れにくく、型安全です。
通常の enum では、列挙子の名前が周囲のスコープに出てしまい、名前衝突が起こりやすくなります。
また、整数との暗黙変換によって、意図しない比較や代入が起きることもあります。
一方、enum class は型が明確で、別の列挙型や整数と混同しにくくなります。
実務では、特別な理由がなければ enum class を使うとよいです。
クラス内の定数
クラス共通の定数には静的メンバを使う
クラスに関連する定数を定義したい場合は、クラスの静的メンバとして定義することがあります。
たとえば、最大サイズ、既定値、上限値、固定設定など、そのクラスに強く関連する値です。
このような値をクラス内に置くと、定数の所属が明確になります。
C++17以降ではinline static constexprが便利
C++17以降では、クラス内の定数には inline static constexpr を使うと扱いやすいです。
これにより、ヘッダ内に定数を定義しやすくなり、複数の翻訳単位から使う場合の問題を避けやすくなります。
従来の static const でも使える場面はありますが、アドレスを取る場合など、実体定義が必要になるケースがありました。
現代C++では、クラスに属するコンパイル時定数には inline static constexpr を使うのが分かりやすい選択です。
グローバル定数
ヘッダに定数を置く場合は注意が必要
グローバル定数をヘッダに置く場合は、リンケージや重複定義に注意が必要です。
C++では、名前空間スコープの const はデフォルトで内部リンケージを持つため、各翻訳単位ごとに別々の定数として扱われることがあります。
これが問題にならない場合もありますが、意図を明確にしたいなら、C++17以降では inline constexpr を使うのが便利です。
グローバル定数は増やしすぎない
グローバル定数は便利ですが、増やしすぎると管理が難しくなります。
定数は、できるだけ意味のある名前空間やクラスに所属させるとよいです。
たとえば、アプリケーション全体の設定値であれば設定用の名前空間に置く。
特定のクラスに関係する値であれば、そのクラスの静的メンバにする。
このように整理すると、コードの見通しがよくなります。
文字列定数
C形式の文字列定数
C++では、文字列リテラルは変更してはいけません。
そのため、文字列リテラルをポインタで扱う場合は、読み取り専用として扱う必要があります。
昔のC風コードでは、文字列リテラルを変更可能な文字列のように扱う書き方を見かけることがありますが、C++では不適切です。
文字列リテラルは基本的に読み取り専用のデータとして扱うべきです。
std::stringによる文字列定数
文字列をオブジェクトとして所有したい場合は、std::string を使います。
std::string に const を付ければ、その文字列オブジェクトは変更できません。
一方、内容を変更したい文字列であれば、const を付けずに std::string を使います。
所有する文字列なのか、読み取り専用なのか、参照だけでよいのかによって使い分けます。
std::string_viewによる読み取り専用ビュー
C++17以降では、std::string_view もよく使われます。
std::string_view は文字列を所有せず、既存の文字列を参照するための軽量な型です。
固定の文字列リテラルを参照するだけなら便利です。
ただし、std::string_view は参照先を所有しません。
そのため、参照先の文字列が先に破棄されると危険です。
特に、関数内で作った一時的な文字列を std::string_view として返すような使い方は避ける必要があります。
配列と定数
配列サイズにはコンパイル時定数が必要
標準C++では、通常の配列サイズにはコンパイル時に決まる値が必要です。
そのため、配列サイズには constexpr がよく使われます。
const でも、コンパイル時に値が決まる形であれば配列サイズに使える場合があります。
しかし、実行時入力で決まる const 変数は、標準C++の通常配列サイズには使えません。
つまり、重要なのは const かどうかではなく、コンパイル時に値が決まるかどうかです。
配列の中身を変更不可にする
配列の要素自体を変更したくない場合は、要素型に const を付けます。
この場合、配列の各要素は変更できません。
固定データ、参照用テーブル、定数リストなどを表すときに使えます。
ただし、現代C++では固定長の配列には std::array を使うことも多いです。
autoとconst
autoではトップレベルのconstが外れることがある
auto を使うときは、const の扱いに注意が必要です。
const な値から auto で新しい変数を作ると、コピーされた変数ではトップレベルの const が外れることがあります。
つまり、元の値が const でも、auto でコピーした新しい変数は変更可能になる場合があります。
新しい変数も変更不可にしたい場合は、const auto と書く必要があります。
参照で受けるならconst auto&がよく使われる
範囲for文などでは、const auto& がよく使われます。
これは、要素をコピーせずに参照し、なおかつ変更しないことを表します。
大きなオブジェクトのリストを走査するときに特に有効です。
実務では、読み取り専用でループする場合に const auto& を使う場面が多いです。
const_cast
constを外すキャスト
C++には、const を外すための const_cast があります。
ただし、これは慎重に使うべき機能です。
元のオブジェクトが本当に const として作られている場合、その const を外して書き換えると未定義動作になります。
一方、元のオブジェクトが非 const であり、一時的に const 参照や const ポインタとして見えているだけなら、const_cast で変更可能に戻せる場合があります。
通常の設計では避ける
const_cast が必要になる場面はあります。
たとえば、古いC APIとの連携や、既存ライブラリとの互換性対応などです。
しかし、通常のC++設計では、const_cast が必要になる時点で設計を見直した方がよい場合が多いです。
const_cast は便利な道具というよりも、慎重に扱うべき例外的な機能と考えるべきです。
constと不変性
constは完全な不変性を保証するとは限らない
C++の const は、必ずしも「その値がプログラム全体で絶対に変わらない」ことを意味しません。
特に参照やポインタでは、const は「その経路から変更できない」という意味になります。
元のオブジェクトが非 const なら、別の経路から変更できる場合があります。
そのため、const は「不変オブジェクト」を作るためだけの機能ではなく、読み取り専用インターフェースを作るための機能でもあります。
API設計で重要になる
const はAPI設計でも重要です。
- 関数が引数を変更しないなら、
const参照で受け取る。 - メンバ関数が状態を変更しないなら、末尾に
constを付ける。 - 戻り値から内部状態を変更させたくないなら、
const参照を返す。
このように const を正しく使うことで、コードを読む人に「ここでは変更されない」という安心感を与えられます。
実務での使い分け
コンパイル時に決まる値はconstexpr
値がコンパイル時に決まるなら、基本的には constexpr を使うとよいです。
たとえば、固定の上限値、配列サイズ、数学定数、設定値などです。
constexpr を使うことで、その値がコンパイル時に評価できることが明確になります。
実行時に決まるが変更しない値はconst
実行時に入力や計算によって値が決まるものの、その後は変更したくない場合は const を使います。
たとえば、ユーザー入力を受け取った後に、その値を処理中ずっと固定したい場合などです。
この場合、コンパイル時定数ではありませんが、プログラム上の安全性を高めるために const を使います。
大きなオブジェクトを読むだけならconst T&
関数に大きなオブジェクトを渡すとき、コピーを避けつつ変更も防ぎたいなら const T& を使います。
文字列、コンテナ、構造体、クラスオブジェクトなどではよく使われます。
ただし、小さな基本型では値渡しで十分なことも多いです。
状態を変更しないメンバ関数には末尾const
クラスのメンバ関数がオブジェクトの状態を変更しないなら、末尾に const を付けます。
これはC++のクラス設計で非常に重要です。
特にgetter、状態確認関数、サイズ取得関数などには、基本的に const を付けるとよいです。
定数の集合にはenum class
状態や種類を表す定数の集合には、enum class を使うとよいです。
単なる整数定数よりも意味が明確で、型安全です。
モード、状態、エラー種別、選択肢などを表すときに適しています。
単純な定数定義に#defineは避ける
C++では、単純な定数定義にマクロを使う必要はほとんどありません。
型安全性、スコープ、デバッグのしやすさを考えると、constexpr や const の方が適しています。
よくある誤解
constなら必ずコンパイル時定数になるわけではない
const は変更不可を表しますが、必ずコンパイル時定数になるわけではありません。
実行時に入力された値で初期化された const 変数は、変更できませんが、コンパイル時には値が分かりません。
コンパイル時に値が必要な場合は、constexpr を使う方が明確です。
constexprは必ず実行時に存在しないわけではない
constexpr はコンパイル時評価を可能にする仕組みですが、状況によっては実行時にも使われます。
特に constexpr 関数は、コンパイル時に評価できる場合はコンパイル時に評価され、実行時の値を渡した場合は実行時に評価されます。
必ずコンパイル時に評価したい場合は、consteval を使います。
const int*とint* constは違う
この2つは初心者が非常に間違えやすいポイントです。
一方は、ポインタ経由で指す先を変更できないという意味です。
もう一方は、ポインタ自体が別の場所を指せないという意味です。
どちらが固定されているのかを意識することが重要です。
値で返す戻り値にconstを付ける必要はほとんどない
値で返す戻り値に const を付けても、多くの場合メリットはありません。
特にクラス型を値で返す場合は、使い勝手や最適化の面で不利になることがあります。
戻り値に const を付けるなら、参照やポインタで返す場合に意味があるかを考えるべきです。
constinitは定数を作るための機能ではない
constinit は名前に const が入っていますが、変更不可を意味しません。
目的は、静的初期化を保証することです。
定数を作りたいなら、基本的には const や constexpr を使います。
まとめ
C++の定数には、複数の仕組みがあります。
最も基本的なのは const です。
これは、値を変更できないようにするために使います。
一方、constexpr はコンパイル時に評価できる値や関数を表します。
コンパイル時に値が必要な場面では、const よりも constexpr の方が意図が明確です。
consteval は、必ずコンパイル時に評価される関数を作るための機能です。
constinit は、静的初期化を保証するための機能であり、変更不可を意味するものではありません。
また、関連する定数の集合には enum class を使うと、型安全で分かりやすいコードになります。
実務では、次のように使い分けるとよいです。
| 目的 | 推奨される使い方 |
|---|---|
| コンパイル時に決まる固定値 | constexpr |
| ヘッダに置く定数 | inline constexpr |
| 実行時に決まるが変更しない値 | const |
| 大きなオブジェクトを読み取り専用で受け取る | const T& |
| 状態を変更しないメンバ関数 | 末尾の const |
| 型安全な定数の集合 | enum class |
| 必ずコンパイル時に評価したい関数 | consteval |
| 静的初期化を保証したい変数 | constinit |
| 単純な定数定義 | #define は基本的に避ける |
C++の定数を理解するうえで最も重要なのは、const と constexpr の違いです。
const は「変更できない」。
constexpr は「コンパイル時に評価できる」。
この違いを押さえたうえで、ポインタ、参照、関数引数、戻り値、メンバ関数での使い方を理解すると、C++の定数はかなり扱いやすくなります。
以上、C++の定数についてでした。
最後までお読みいただき、ありがとうございました。
