Replacing text macros
プリプロセッサはテキストマクロ置換をサポートしています。関数形式のテキストマクロ置換もサポートされています。
目次構文
説明(注:元のテキストには翻訳対象となる自然言語のテキストが含まれていないため、HTML構造のみを保持したまま出力しています) #define ディレクティブ#define ディレクティブは identifier をマクロとして定義し、つまりコンパイラに対して identifier の連続するほとんどの出現を replacement-list で置き換えるよう指示します。この置換リストはさらに処理されます。例外は scanning and replacement の規則から生じます。識別子が既に何らかのタイプのマクロとして定義されている場合、定義が同一でない限りプログラムは不適格です。 オブジェクト形式マクロオブジェクト形式マクロは、定義された identifier のすべての出現を replacement-list で置き換えます。 #define ディレクティブのバージョン(1)はこの動作を正確に実装しています。 関数形式マクロ関数形式マクロは、定義された identifier の各出現箇所を replacement-list で置換し、さらに引数を取り、 replacement-list 内の対応する parameters の出現箇所を置換します。 関数形式マクロの呼び出しの構文は関数呼び出しの構文と類似しています:マクロ名の各インスタンスの直後に ( が次のプリプロセッシングトークンとして続く場合、そのトークン列は replacement-list によって置換されるトークン列の開始を導入します。このシーケンスは対応する ) トークンによって終了し、途中に現れる一致する左右の括弧のペアはスキップされます。
バージョン(2)では、引数の数はマクロ定義内のパラメータ数と同じでなければなりません。バージョン(3,4)では、引数の数はパラメータ数より少なくてはなりません(
以下
(C++20以降)
バージョン(2)の #define ディレクティブは、単純な関数形式マクロを定義します。
バージョン(3)の
#define
ディレクティブは、可変長引数を持つ関数形式マクロを定義します。追加の引数(
可変引数
と呼ばれる)は
バージョン(4)の
#define
ディレクティブは、可変長引数を持つ関数形式マクロを定義しますが、通常の引数は持ちません。引数(
可変引数
と呼ばれる)は
注意: 関数形式マクロの引数に、対応する左右の括弧で保護されていないカンマ(最も一般的にはテンプレート引数リスト内で見られる、例えば assert ( std:: is_same_v < int , int > ) ; または BOOST_FOREACH ( std:: pair < int , int > p, m ) )が含まれている場合、そのカンマはマクロ引数の区切り文字として解釈され、引数の数が合わないためにコンパイルエラーが発生します。 スキャニングとリプレースメント
疑似再帰マクロを定義することが可能であることに注意してください:
このコードを実行
#define EMPTY #define SCAN(x) x #define EXAMPLE_() EXAMPLE #define EXAMPLE(n) EXAMPLE_ EMPTY()(n-1) (n) EXAMPLE(5) SCAN(EXAMPLE(5)) 出力: EXAMPLE_ ()(5 -1) (5) EXAMPLE_ ()(5 -1 -1) (5 -1) (5) 予約済みマクロ名標準ライブラリヘッダを インクルードする翻訳単位 は、いかなる 標準ライブラリヘッダ で宣言されている名前を #define または #undef してはならない。 標準ライブラリのいずれかの部分を使用する翻訳単位は、以下のものと字句的に同一の名前を #define または #undef してはなりません:
それ以外の場合、動作は未定義です。 (注:指定された要素内には翻訳対象となるテキストコンテンツが存在しないため、HTML構造のみが保持されています) # および ## 演算子関数形式マクロにおいて、 # 演算子を識別子の前に置くと、 replacement-list 内の識別子がパラメータ置換を経て引用符で囲まれ、実質的に文字列リテラルが生成されます。さらに、プリプロセッサは埋め込まれた文字列リテラル(存在する場合)を囲む引用符をエスケープするためにバックスラッシュを追加し、必要に応じて文字列内のバックスラッシュを二重化します。すべての先頭および末尾の空白は削除され、テキスト中間の連続する空白(ただし埋め込まれた文字列リテラル内を除く)は単一のスペースに圧縮されます。この操作は「文字列化(stringification)」と呼ばれます。文字列化の結果が有効な文字列リテラルでない場合、動作は未定義となります。
任意の2つの連続する識別子の間にある
##
演算子は、2つの識別子(マクロ展開前の状態)に対してパラメータ置換を実行し、その結果を連結します。この操作は「連結」または「トークン貼り付け」と呼ばれます。有効なトークンを形成するトークンのみが貼り付け可能です:より長い識別子を形成する識別子、数値を形成する数字、または
注意: 一部のコンパイラは、カンマの後かつ
#undef ディレクティブ#undef ディレクティブは 識別子 の定義を解除します。つまり、 #define ディレクティブによる以前の 識別子 の定義を取り消します。識別子に関連付けられたマクロが存在しない場合、このディレクティブは無視されます。 定義済みマクロ以下のマクロ名は、すべての翻訳単位で定義済みです:
以下の追加のマクロ名は実装によって定義済みの場合があります:
これらのマクロの値(
例
このコードを実行
#include <iostream> // 関数ファクトリを作成して使用 #define FUNCTION(name, a) int fun_##name() { return a; } FUNCTION(abcd, 12) FUNCTION(fff, 2) FUNCTION(qqq, 23) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) std::cout << "output: " #a << '\n' // 後のマクロ定義でマクロを使用 #define WORD "Hello " #define OUTER(...) WORD #__VA_ARGS__ int main() { std::cout << "abcd: " << fun_abcd() << '\n'; std::cout << "fff: " << fun_fff() << '\n'; std::cout << "qqq: " << fun_qqq() << '\n'; std::cout << FUNCTION << '\n'; OUTPUT(million); //引用符がないことに注意 std::cout << OUTER(World) << '\n'; std::cout << OUTER(WORD World) << '\n'; } 出力: abcd: 12 fff: 2 qqq: 23 34 output: million Hello World Hello WORD World 不具合報告以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
関連項目
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||