マクロの使用をどこで好むべきで、constexprをどこで好むべきですか?基本的に同じではないですか?
#define MAX_HEIGHT 720
vs
constexpr unsigned int max_height = 720;
マクロの使用をどこで好むべきで、constexprをどこで好むべきですか?基本的に同じではないですか?
#define MAX_HEIGHT 720
vs
constexpr unsigned int max_height = 720;
回答:
基本的に同じではないですか?
いいえ、絶対にありません。程遠い。
あなたのマクロがでありint
、あなたconstexpr unsigned
がであるという事実とは別に、unsigned
重要な違いがあり、マクロには1つの利点しかありません。
マクロはプリプロセッサによって定義され、発生するたびにコードに置き換えられるだけです。プリプロセッサはばかげており、C ++の構文やセマンティクスを理解していません。マクロは名前空間、クラス、ファンクションブロックなどのスコープを無視するため、ソースファイル内の他の名前を使用することはできません。これは、適切なC ++変数として定義された定数には当てはまりません。
#define MAX_HEIGHT 720
constexpr int max_height = 720;
class Window {
// ...
int max_height;
};
max_height
クラスメンバーであり、スコープが異なり、名前空間スコープのものとは異なるため、メンバー変数を呼び出すことは問題ありません。MAX_HEIGHT
メンバーの名前を再利用しようとすると、プリプロセッサはそれをコンパイルされないこのナンセンスに変更します。
class Window {
// ...
int 720;
};
これが、マクロUGLY_SHOUTY_NAMES
を目立たせるために指定する必要がある理由であり、衝突を避けるためにマクロに名前を付ける際には注意が必要です。マクロを不必要に使用しない場合は、それについて心配する必要はありません(そして読む必要もありませんSHOUTY_NAMES
)。
関数内に定数が必要な場合は、マクロを使用してそれを行うことはできません。プリプロセッサは関数が何であるか、または関数内にあることの意味を知らないためです。マクロをファイルの特定の部分のみに制限するには、マクロを#undef
再度制限する必要があります。
int limit(int height) {
#define MAX_HEIGHT 720
return std::max(height, MAX_HEIGHT);
#undef MAX_HEIGHT
}
はるかに賢明なものと比較してください:
int limit(int height) {
constexpr int max_height = 720;
return std::max(height, max_height);
}
なぜあなたはマクロのものを好むのですか?
constexpr変数は変数であるため、実際にはプログラムに存在し、アドレスを取得して参照をバインドするなど、通常のC ++処理を実行できます。
このコードの動作は未定義です。
#define MAX_HEIGHT 720
int limit(int height) {
const int& h = std::max(height, MAX_HEIGHT);
// ...
return h;
}
問題はそれMAX_HEIGHT
が変数ではないことです。そのため、std::max
一時的な呼び出しint
はコンパイラーによって作成される必要があります。によって返される参照はstd::max
、そのステートメントの終了後に存在しない一時的なものを参照する可能性があるため、return h
無効なメモリにアクセスします。
この問題は、適切な変数では存在しません。これは、メモリ内の固定された場所が消えないためです。
int limit(int height) {
constexpr int max_height = 720;
const int& h = std::max(height, max_height);
// ...
return h;
}
(実際には、おそらく宣言しint h
ないでしょうconst int& h
が、問題はより微妙な状況で発生する可能性があります。)
マクロを優先するのは、#if
条件で使用するために、プリプロセッサがその値を理解する必要がある場合のみです。
#define MAX_HEIGHT 720
#if MAX_HEIGHT < 256
using height_type = unsigned char;
#else
using height_type = unsigned int;
#endif
プリプロセッサが変数を名前で参照する方法を理解していないため、ここでは変数を使用できませんでした。マクロ展開や#
(#include
and#define
や#if
)で始まるディレクティブなどの基本的な非常に基本的なことだけを理解します。
プリプロセッサが理解できる定数が必要な場合は、プリプロセッサを使用して定義する必要があります。通常のC ++コードの定数が必要な場合は、通常のC ++コードを使用してください。
上記の例は、プリプロセッサの状態を示すためのものですが、そのコードでもプリプロセッサの使用を回避できます。
using height_type = std::conditional_t<max_height < 256, unsigned char, unsigned int>;
constexpr
そのアドレス(ポインタ/参照)が行われるまでメモリを占有しない可変必要。そうでなければ、完全に最適化することができます(そしてそれを保証するStandardeseがあるかもしれません)。これを強調したいのは、ストレージを必要としないenum
些細なconstexpr
ことでも一部を占めるという誤った考えから、人々が古い劣った「ハック」を使い続けないようにするためです。
int height
場合、そのスコープは関数に関連付けられており、本質的に一時的であるため、マクロと同じように問題になります。3.上記のコメント「constint&hは一時的なものの寿命を延ばします」は正しいです。
limit
問題はの戻り値ですstd::max
。2.はい、それが参照を返さない理由です。3.間違っています。上記のcoliruリンクを参照してください。
const int& h = max(x, y);
、max
その値で返す場合、その戻り値の寿命は延長されます。戻り値の型ではなく、const int&
それにバインドされています。私が書いたことは正しいです。
一般的に言って、constexpr
マクロは他の解決策が不可能な場合にのみ使用する必要があります。
マクロはコード内の単純な置換であり、このため、多くの場合、競合が発生します(たとえば、windows.hmax
マクロとstd::max
)。さらに、機能するマクロは別の方法で簡単に使用でき、奇妙なコンパイルエラーを引き起こす可能性があります。(例:Q_PROPERTY
構造体メンバーで使用)
これらすべての不確実性のため、通常のgotoを回避するのとまったく同じように、マクロを回避することは適切なコードスタイルです。
constexpr
は意味的に定義されているため、通常、発生する問題ははるかに少なくなります。
#if
つまり、プリプロセッサが実際に役立つものを使用した条件付きコンパイル。定数の定義は、プリプロセッサ条件で使用されるため、その定数がマクロである必要がある場合を除いて、プリプロセッサが役立つものの1つではありません#if
。定数が通常のC ++コード(プリプロセッサディレクティブではない)で使用する場合は、プリプロセッサマクロではなく通常のC ++変数を使用します。
JonathonWakelyによる素晴らしい答え。また、私は見てとることをアドバイスしたいjogojapanの回答差が間にあるものになどconst
とconstexpr
、あなたもマクロの使用を考慮して行く前に。
マクロはばかげていますが、良い意味で。表面上、最近では、特定のビルドパラメータが「定義」されている場合にのみ、コードの非常に特定の部分をコンパイルする場合のビルドエイドです。通常は、すべてその手段マクロの名前を取っている、またはより良いまだ、のは、それを呼び出してみましょうTrigger
、と、のようなものの追加/D:Trigger
、-DTrigger
使用されているビルドツールになど、。
マクロにはさまざまな用途がありますが、私が最もよく目にするのは、悪い/時代遅れの慣行ではない2つです。
したがって、OPの場合、intをconstexpr
またはで定義するという同じ目標を達成できますが、MACRO
最新の規則を使用する場合、2つが重複する可能性はほとんどありません。まだ段階的に廃止されていない一般的なマクロの使用法を次に示します。
#if defined VERBOSE || defined DEBUG || defined MSG_ALL
// Verbose message-handling code here
#endif
マクロ使用の別の例として、リリースする予定のハードウェアがある場合、または他の人が必要としないいくつかのトリッキーな回避策がある特定の世代があるとします。このマクロをとして定義しますGEN_3_HW
。
#if defined GEN_3_HW && defined _WIN64
// Windows-only special handling for 64-bit upcoming hardware
#elif defined GEN_3_HW && defined __APPLE__
// Special handling for macs on the new hardware
#elif !defined _WIN32 && !defined __linux__ && !defined __APPLE__ && !defined __ANDROID__ && !defined __unix__ && !defined __arm__
// Greetings, Outlander! ;)
#else
// Generic handling
#endif