デフォルトの配置new
演算子は、18.6 [support.dynamic] ¶1 でスローしない例外仕様で宣言されています。
void* operator new (std::size_t size, void* ptr) noexcept;
ただし、5.3.4 [expr.new] ¶15によれreturn ptr;
ばnoexcept
、これは、オブジェクトのコンストラクターを呼び出す前に、コンパイラーが null を返さないことを確認する必要があることを意味します。
-15-
[注:例外をスローしない例外仕様 (15.4) で割り当て関数が宣言されていない限り、例外をスローしてストレージの割り当てに失敗したことを示しstd::bad_alloc
ます (第 15 節、18.6.2.1)。それ以外の場合は、null 以外のポインターを返します。割り当て関数が例外をスローしない仕様で宣言されている場合、記憶域の割り当てに失敗したことを示すために null を返し、それ以外の場合は null 以外のポインターを返します。割り当て関数が null を返す場合、初期化は行われず、割り当て解除関数は呼び出されず、new-expression の値は null になります。
(一般的にではなく、具体的には Placementnew
の場合)この null チェックは、小さいながらも残念ながらパフォーマンスに影響を与えるようです。
new
コンパイラのコード生成を改善するために、パフォーマンスが非常に重要なコード パスで配置が使用されているコードをデバッグしており、アセンブリで null のチェックが観察されました。例外をスローする仕様で宣言されたクラス固有の配置new
オーバーロードを提供することにより (スローできない可能性がありますが)、条件分岐が削除され、コンパイラは周囲のインライン関数に対してより小さなコードを生成することもできました。配置new
関数がスローできなくても、スローできると言う結果は、測定可能なほど優れたコードでした。
そのため、配置の場合にnullチェックが本当に必要かどうか疑問に思っていましたnew
。null を返すことができる唯一の方法は、null を渡す場合です。次のように書くことは可能であり、明らかに合法ですが、
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
なぜそれが役立つのかわかりません。プログラマーが配置を使用する前に null を明示的にチェックする必要がある場合は、より良いと思いますnew
。
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
new
null ポインターのケースを正しく処理するために配置が必要だった人はいますか? (つまりptr
、有効なメモリ位置である明示的なチェックを追加しません。)
new
null ポインターをデフォルトの配置関数に渡すことを禁止することが合理的かどうか、もしそうでない場合は、コンパイラーに値が null ではないことを伝えようとする以外に、不要な分岐を回避するためのより良い方法があるかどうか疑問に思っています。
void* ptr = getAddress();
(void) *(Obj*)ptr; // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();
または:
void* ptr = getAddress();
if (!ptr)
__builtin_unreachable(); // same, but not portable
Obj* obj = new (ptr) Obj();
注意: この質問は意図的にマイクロ最適化にタグ付けされています。パフォーマンスを「改善」するために、すべてのタイプの配置をオーバーロードすることをお勧めしません。new
この影響は、非常に特定のパフォーマンスが重要なケースで、プロファイリングと測定に基づいて確認されました。
更新: DR 1748により、null ポインターを新しい配置で使用する動作が未定義になるため、コンパイラーはチェックを行う必要がなくなりました。