キーワード (C++)

本記事では、プログラミング言語C++におけるキーワードについて解説する。

本記事は、あくまでC++のキーワードに焦点をあてた記事であり、C++の全体像や、C++のキーワード以外の面には立ち入らない。読者の理解の助けとなる場合は適宜、他のプログラミング言語と比較する説明は行う。

なおC++のものに限らず、キーワード（）、予約語（）、予約済みの識別子（）といった概念全般や細かな違いについては「予約語」の記事を参照のこと。

 C++のキーワードの特徴 
最初に制定された規格であるISO/IEC 14882:1998（通称C++98）では63個のキーワードと11個の代替表現（）が採用され、ISO/IEC 14882:2003（通称C++03）では追加のキーワードはなかった。ISO/IEC 14882:2011（通称C++11）で新たに13個のキーワードおよび2個の文脈依存キーワード（後述）が追加された。C++11から追加のキーワードおよび機能には(*)を付す。C++14およびC++17ではキーワードは増えていない。

C言語のC89/C90で存在したキーワードは、すべてがほぼそのままの形で取り入れられている。C95で追加されたwchar_tはキーワードではなかったが、C++ではキーワードとなった。中には、inlineなど最初C++98に導入されたキーワードが後からC99にも導入されたものもある。C11では同時期に策定されたC++11の, , , に直接対応するキーワードは追加されず、, , , というキーワードが追加され、C++互換の名前に関しては別途マクロとして提供するヘッダーが追加された。C23では改めてC++同様のキーワードが追加され、C11で追加された古いキーワードの多くは非推奨となる予定である。

 値に関するキーワード 

 nullptr (*) 
ヌルポインタ () を表す。この値はあるstd::nullptr_t型のオブジェクトで、任意のポインタ型のヌルポインタ値に暗黙変換できるが、整数型には暗黙変換できない。なお、従来のNULLマクロは、もともとC++98の時点で実装定義のヌルポインタ定数 () に展開される仕様だったが、C++ではヌルポインタ定数の1つとして整数リテラル0を任意のポインタに代入することができるため、C++03までは整数リテラル0に展開される実装が一般的だった。C++11以降はnullptrに展開される実装もありえることになる。

 型に関するキーワード 
複数のキーワードを組み合わせることで意味が変わるものが多い（文脈依存のキーワード）。

 基本型 

 int, long, short, signed, unsigned 

short単体では符号付き短整数型signed short intを、long単体では符号付き長整数型signed long intを意味する。long long intは少なくとも64ビットの精度を持つ整数型である(*)。

 float, double 

long doubleは少なくともdoubleと同じ精度を持つ浮動小数点数型。処理系によっては拡張倍精度浮動小数点数型となる。

 bool, true, false 

C99ではマクロで定義されておりキーワードではないが、C++ではキーワードである。

 char, wchar_t, char16_t (*), char32_t (*) 

wchar_tは、Cではtypedefによる型エイリアスでありキーワードでないが、C++ではキーワードである。

char16_tとchar32_tは、それぞれUTF-16およびUTF-32の文字を格納するための整数型を表す。

void

戻り値や引数がないことを表す型。
デリファレンスする先の型を指定しないポインタvoid*の表現に用いられる。

auto (*)
変数宣言の際に型として指定すると、初期化式から型推論を行う。

また、戻り値型を後置きする関数宣言に用いる。
// 以下の2つは同じ宣言。
int main(int argc, char** argv);
auto main(int argc, char** argv) -> int;

 複合型 

 class, struct, union 

C++においてstructはデフォルトのアクセス制御がpublicであること以外はclassと全く同じである。unionもクラスの一種という扱いである。

unionはすべてのメンバ変数が同一メモリ領域を共有するのはCのままであるが、classに準じてメンバ関数を持てるようになっている。

なお、C++規格では、クラス内で宣言した変数および関数は、それぞれデータメンバ（）およびメンバ関数（）と呼ぶ。データメンバはメンバ変数と呼ばれることも多い。JavaやC#のように、フィールド・メソッドなどといった用語は使われない。

 enum 

enum単独ではスコープのない列挙型を意味する。enum class、enum structの形で、スコープ付きの列挙型の宣言に使用する(*)。

 型修飾子 

 const 

Cの書き換え不能という意味に加え、定数の意味が加わっている。具体的にはCと違いブロックスコープにない場合、定数扱いとなり、externを指定しない限り内部結合になる。プリプロセッサによるマクロの代替的手段になる。
const int N = 16;
int Array[N];
このコード片で、CではNが定数ではないのでArrayの宣言はエラーになる。C99でもグローバル変数は可変長配列にできないのでやはりエラーになる。
クラス内では、整数型をstatic constで修飾するとクラススコープの定数の定義になる。詳しくはstaticを参照のこと。

 volatile 

volatileの指定された記憶領域は、実行しているコードが書き換えを行わない場合にも、何らかの理由で内容が書き換えられる可能性を持つ。書き換えの起きる具体的な要因としては、何らかのハードウェアによる書き換えが挙げられる。

コンパイラによる最適化では、メモリを書き換えない限り内容が変わらないことを利用し、メモリからの再読み込みの省略などを行うが、例えばあるメモリ領域にハードウェアが書き込んでいて、そのメモリ領域の変化を読み取りループで監視しようとするならば、volatile指定が無い場合はコンパイラが「書き込みを行っていないのでメモリ内容が変化する可能性は無い」と判断し、ループを消し去ったり、メモリを再読み込みせずに単純な無限ループを行ったりするなど、期待と異なる最適化を行う可能性が高い。そのような場合、volatileを適切に指定することで、メモリ領域を正しくポーリングすることが期待できる。

また、コンパイラによっては、volatileの指定された記憶領域へのアクセス前後において、適切なメモリバリアを自動的に配置することもある。が、逆にメモリバリアについては一切関知しないものもあるため、ノンブロッキング同期プログラミングなどでは特別な注意が必要となる。

ハードウェアに近い操作を行う処理を記述する際は、特に重要になる修飾子である。

 記憶クラス指定子 

 auto (C++03まで) 
C++03以前では自動変数 () を意味する記憶クラス指定子 () であったが、C++11で廃止され、型推論のための全く別の機能が割り当てられた。

もともと、関数のようなブロックスコープ内の変数（ローカル変数）に記憶クラスを指定しなかった場合はデフォルトで自動記憶域期間 () を持つ自動変数となるため、C++03以前においても記憶クラス指定子としてのautoを用いる必要は全くなかった。前方互換性のためにも、むやみに使用すべきではない。なお、C++11以降も自動記憶域期間を持つ変数を表す用語は「自動変数」のままであり変更はない。

 extern 
Cからの外部結合の指定に加え、リンケージ指定の用法が加わっている。

C++では名前（関数名や変数名など）に対して多重定義や名前空間、型安全の保証などの都合から、多くのコンパイラは名前修飾を施しCとは異なった名前をリンカに対して用いている。その名前の修飾の仕方を指定するのがリンケージ指定である。少なくとも"C"と"C++"の2種類のリンケージが使用できる。何も指定しないと"C++"になる。"C"リンケージでは名前の変形を抑止しCと互換の名前をリンカに対して用いることを意味する。これによりCとC++を混在させてプログラムを作るときに使われる。
//a.cpp
extern "C" {
 int a; //Cリンケージの変数定義
 extern int b; //Cリンケージの変数宣言
 void f(); //Cリンケージの関数宣言
}
extern "C" int c; //リンケージ指定と元々の外部結合の意味を兼ねた用法。

//b.c
extern int a; //変数の宣言
int b; //変数の定義
int c;
void f()
{
 /* ... */
}

また、extern templateの形でテンプレート実体化宣言を修飾して、外部で実体化されたテンプレートを指定する(*)。

 register 
C++17以降では何の機能もないキーワードである。将来のために予約されている。

C++14以前では、特にレジスタに割り当ててほしい自動変数に指定する記憶クラス指定子であった。Cよりも意味合いが弱く、Cのregister変数はアドレス参照（&演算子によるポインタ取得）を行えないが、C++では行えた。つまり、register変数として宣言してもメモリに配置されうる。コンパイラが高度な最適化を行う場合、レジスタに配置すべき変数の判断はコンパイラの方が適切に行えることが多いので、C++14以前のコンパイラでも、この指定を無視するのが一般的であった。

 static 
ファイルスコープの指定（Cからの用法）。ただし、ファイルスコープを指定するのにstaticを用いるのはC++では推奨されず、無名名前空間を用いるべきとされている。
クラス・関数の静的変数とクラスの静的関数（関数内静的変数についてはCからの用法）。
特にクラスの場合、これは宣言となるため普通のグローバル変数などと同様にソースファイルで定義が必要である。ただし次の場合、例外として不要である。
整数型にconstと共に指定し宣言と同時に初期化した場合。クラススコープの定数となる。
クラスの静的メンバ関数の場合。（クラス宣言とは別にソースファイルで定義をしてもよいし、クラス内にインラインで定義してもよい）
// ヘッダbar.h
class bar
{
 static int a; // OK: 静的メンバ a の宣言。
 static const int b = 5; // OK: 静的メンバ b の宣言と定義。クラススコープの定数となる。
 static const double c; // OK: 静的メンバ c の宣言。
// static const double d = 2.0; // Bad: 整数型でない。
 static void f(); // OK: 静的メンバ f の宣言。
 static void g() { /* ... */ } // OK: 静的メンバ g の宣言と定義。
};

// ソースbar.cpp
#include "bar.h"

int bar::a = 1; // OK: bar::a の定義。

//const int bar::b = 5; // クラススコープの定数は定義する必要がない。

const double bar::c = 3.0; // OK: bar::c の定義。

void bar::f() // OK: bar::f の定義。
{
 /* ... */
}

古いC++処理系では整数型をstatic constで修飾することによる定数の定義ができなかったので、代わりにクラス内に定義した列挙型 (enum) による列挙定数で代用をすることがしばしばあった。これはenumハックと呼ばれる。

C++11以降はconstexprを併用することで、クラス宣言部で定数定義できる型が増加した。

 mutable 
constなオブジェクトでも書き換え可能にしたいクラスメンバ変数に指定する。
ラムダ式を修飾して、コピーキャプチャした変数の書き換えを許可する(*)。

 thread_local (*) 
グローバル変数やクラス・関数内静的変数について、スレッドローカルな記憶域に確保するよう指示する。

 宣言指定子 

 friend 
フレンド関数・フレンドクラスの宣言に用いる。

 typedef 

 関数指定子 

 constexpr (*) 
定数式を表す。変数や関数を修飾して、コンパイル時定数となりうることを指示する。

 explicit 
コンストラクタに指定して暗黙の型変換に用いないことを指定する。
変換関数を修飾して、暗黙の型変換に用いないことを指定する(*)。

 inline 
関数指示子に使用することで、関数をインライン展開するためのヒントをコンパイラに与える。クラスの宣言内で関数を定義した場合は、inlineの有無にかかわらずinlineが指定されたものとして取り扱われる。
inline namespaceの形で透過的な名前空間の宣言に用いる。透過的な名前空間の名前は、名前空間名で修飾しなくても使えるようになる(*)。

 virtual 
仮想関数を宣言する。C++ではこれを明示しないと派生クラスでポリモーフィックにオーバーライドすることはできない。派生クラスではオーバーライドするときに特に何か指定する必要はないが、virtualを省略しないスタイルも一般的である。
仮想継承するときにベースクラスの指定に修飾する。

クラスに関するキーワード

アクセス制御

public, protected, private
クラスメンバと継承のアクセス制御に用いられる。
publicはクラスの外部からも自由にアクセスできる。
protectedは宣言したクラス自身とその派生クラスからアクセスできる。
privateは宣言したクラスの内部からしかアクセスできない。

Javaなどとは違い個々の宣言に付けるものではなく、見た目には関数内でラベルを指定するのと似ている。1度指定すると他の指定がなされるか、クラスの宣言が終わるまでは指定した制御が続く。
class foo
{
 int n; //classであり、まだアクセス制御が指定されていないのでprivateになる
public:
 foo(int); //コンストラクタ
 ~foo(); //デストラクタ

 int getN(); //メンバ関数
 //ここまでの3つはすべてpublicになる
protected:
 void setN(int); //protectedになる
}; //クラス宣言の終わりには ; が必要。

class hoge : private foo //private継承 外部からはhogeのインスタンスを通じてfooのメンバにはアクセスできない。
{
public:
 using foo::getN; //このように個別にアクセス制御を上書きすることもできる。
 hoge();
 ~hoge();
};