C/C++でconstの位置に迷うときの考え方

プログラミングC/C++

C/C++で、ポインタや参照が絡んだ時に const をどこにつければいいか一瞬迷うことがある。そのため、 const の考え方について自分用にメモする。

constの考え方

覚えておくべきルールは以下の2つだけである。

  1. 冒頭のconstは直後の型名と入れ替えても同じ意味
    • const intint constは同じ型
    • const int*int const*は同じ型だがint* constとは違う型
  2. constvolatile*&&&は直前の型を修飾して別の型にする記号
    • 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を付ける位置に迷うことは少なくなるはずだ1

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;
}

とてもややこしいので注意が必要だ2

constexprconst の関係については以下の記事も参照。

notes

  1. この例題で15分溶かした自称C++プログラマーがいるらしい
  2. constexprでもconstの1.のルールは使える。すなわち、constexpr int xint constexpr x と書くこともできる

プログラミングC/C++

Posted by komori