31

C++ FAQ によると、マクロは悪です:

[9.5] 単純な古い #define マクロの代わりにインライン関数を使用する必要があるのはなぜですか?

#defineマクロは、悪#1、悪#2、悪#3、悪#4 の 4 つの異なる方法で悪だからです。いずれにせよそれらを使用する必要がある場合もありますが、それでも悪です。マクロとは異なり#define、インライン関数は常にすべての引数を 1 回だけ評価するため、悪名高いマクロ エラーを回避します。つまり、インライン関数の呼び出しは意味的には通常の関数の呼び出しと同じですが、より高速です。

// A macro that returns the absolute value of i
#define unsafe(i)  \
        ( (i) >= 0 ? (i) : -(i) )

// An inline function that returns the absolute value of i
inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
  int ans;

  ans = unsafe(x++);   // Error! x is incremented twice
  ans = unsafe(f());   // Danger! f() is called twice

  ans = safe(x++);     // Correct! x is incremented once
  ans = safe(f());     // Correct! f() is called once
}

また、マクロとは異なり、引数の型がチェックされ、必要な変換が正しく実行されます。

マクロは健康に悪いです。必要がない限り、それらを使用しないでください。

unsafe(x++)インクリメントが 2 回ある理由を誰かが説明できますかx? 私は理解することができません。

4

4 に答える 4

69

プリプロセッサを介して実行すると、問題が表示されます。を使用して(gcc -Eオプションも生成された行を抑制する を使用することもできます)、cpp -P-P#

inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
  int ans;

  //    increment 1      increment 2 (one of these)
  //        |             |     |
  //        V             V     V
  ans = ( (x++) >= 0 ? (x++) : -(x++) );
  ans = ( (f()) >= 0 ? (f()) : -(f()) );

  ans = safe(x++);
  ans = safe(f());
}

野蛮なノイズのメモとして、この関数f()unsafeマクロによって 2 回呼び出されます。おそらくそれは純粋な(副作用がない) ので、それ自体は間違っていません。しかし、まだ最適ではありません。

したがって、インライン関数は、他の基本要素 (変数や式) と同じセマンティック レベルで動作するため、一般に関数のようなマクロよりも安全です。また、マニフェスト定数の場合、enums はより整然としたものになることがよくあります。マクロの良い使い方は何ですか?

コンパイル時にのみ認識される定数の設定。コンパイル時にコマンドラインからマクロを定義できます。それ以外の

#define X 12

ソースファイルで、追加できます

-DX=12

ccコマンドに。#undef Xを使用してコマンドラインからもできます-UX

これにより、条件付きコンパイルなどが可能になります。

#if X
   do this;
#else
   do that;
#endif
   while (loop);

おそらくconfigureスクリプトで生成されたメイクファイルによって制御されます。

X-マクロ. X-Macros (IMO) の最も説得力のある用途は、enum識別子を印刷可能な文字列に関連付けることです。最初は面白く見えますが、この種の並列定義による重複と同期の問題が軽減されます。

#define NAMES(_) _(Alice) _(Bob) _(Caravaggio) _(DuncanIdaho)
#define BARE(_) _ ,
#define STRG(_) #_ ,
enum { NAMES(BARE) };
char *names[] = { NAMES(STRG) };

マクロの名前を引数として別のマクロに渡し、引数をそれ自体がマクロであるかのように使用して、渡されたマクロを呼び出すことができることに注意してください(マクロであるため)。X-Macros の詳細については、この質問を参照してください。

于 2013-04-26T19:06:02.040 に答える
17

マクロは、プログラムがコンパイルされる前に効果的にコピー/貼り付けを行います。

unsafe(x++)

なるだろう

( (x++) >= 0 ? (x++) : -(x++) )
于 2013-04-26T19:05:36.460 に答える
10

プリプロセッサは、コンパイル前にマクロを置き換えます。

コンパイラはこれを見ます:

  ( (x++) >= 0 ? (x++) : -(x++) )
于 2013-04-26T19:05:29.920 に答える