簡単なオブジェクトの場合、 `this`で新しい配置を呼び出しても安全ですか?


20

この質問はすでに何度か尋ねられたことは知っていますが、この特定のケースに対する答えは見つかりませんでした。

リソースを所有しておらず、空のデストラクタとデフォルトのコンストラクタを持つ簡単なクラスがあるとします。クラス内で初期化されるメンバー変数がいくつかあります。それらの1つはありませんconst

そのようなクラスのオブジェクトを再初期化し、deInit手動でメソッドを記述せずにそれをしたいと思います。このようにしても安全ですか?

void A::deInit()
{
  new (this)A{};
}

問題はありません。オブジェクトは常に有効な状態でありthis、同じアドレスを指しています。C ++なので、確認したい。


2
オブジェクトconstのメンバーはありますか?
NathanOliver

2
これが有効な場合、それは*this = A{};
ケビン

2
@Amomum *this = A{};は、this->operator=(A{});つまり、一時オブジェクトを作成してに割り当てることを意味し、*thisすべてのデータメンバーの値を一時オブジェクトの値で置き換えます。それはあなたが望むものであり、(私の意見では)新規の配置よりも読みやすいので、代わりにそれを使います。
ケビン、

1
@ケビンああ、私の悪い、あなたは正しい。より- コピーが省略されている場合、それは同等であると思いますか?
Amomum

1
クラスを言葉で説明するのではなく、1つのメソッドだけでなく、完全なクラスを記述する方がはるかに優れています。sscce.orgを
B –овић

回答:


17

の合法性と同様に、へのdelete this新しい配置thisも私の知る限り許可されます。また、、thisまたは他の既存のポインタ/参照を後で使用できるかどうかに関して、いくつかの制限があります:

[basic.life]

オブジェクトの存続期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトが占有していたストレージの場所に新しいオブジェクトが作成された場合、元のオブジェクトを指すポインター、参照元のオブジェクトを参照した場合、または元のオブジェクトの名前が自動的に新しいオブジェクトを参照し、新しいオブジェクトの存続期間が開始すると、次の場合に新しいオブジェクトを操作できます。

  • 新しいオブジェクトのストレージは、元のオブジェクトが占めていたストレージロケーションを正確にオーバーレイします。
  • 新しいオブジェクトは元のオブジェクトと同じタイプです(トップレベルのcv-qualifiersは無視されます)。
  • 元のオブジェクトの型がconst修飾されておらず、クラス型の場合、型がconst修飾型または参照型の非静的データメンバーを含んでいない。
  • 元のオブジェクトも新しいオブジェクトも、重複する可能性のあるサブオブジェクト([intro.object])ではありません。

この例では最初の2つは満たされていますが、最後の2つを考慮する必要があります。

3番目の点に関しては、関数が非const修飾されている場合、元のオブジェクトが非constであると想定することはかなり安全です。誠実さが捨てられた場合、障害は呼び出し側にあります。const / referenceメンバーについては、これが割り当て可能であると主張することで確認できると思います。

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

もちろん、割り当て可能性が要件であるため、代わりに*this = {};、同じプログラムを作成することを期待しているものを単に使用することもできます。おそらくもっと興味深いユースケースは*this、別のタイプのオブジェクトのためにのメモリを再利用することかもしれません(this少なくとも再解釈+ロンダリングなしでは、を使用するための要件に失敗します)。

と同様にdelete this、への新しい配置thisは、「安全」とは言えません。


面白い。この条件がすべて、いくつかの型の特性のstatic_assertで満たされていることを確認することは可能ですか?constメンバーについて1つあるかどうかわからない...
Amomum

1
@Amomumサブオブジェクトであることはテストできるものではないと思います。constまたはreferenceメンバーは、クラスを割り当て不可能にします。これは、ささいなクラスでは他の方法では起こり得ないと思います。
eerorika

これは、constnessに関して厳密なエイリアシングが機能することを意味しますか これが機能する例を提供できますか?
darune

1
@darune constオブジェクトを作成し、配置オブジェクトをその上に配置し、オブジェクトの元の名前(または既存のポインターまたは参照)を使用すると、動作は未定義になります。厳密にエイリアシングを最適化するのとほぼ同じですが、まったく同じではありません。
eerorika

1
の逆はdelete ptrですnew T()。の逆はnew(ptr)T{}ですptr->~T();stackoverflow.com/a/8918942/845092
Mooing Duck '29

7

これをカバーするルールは[basic.life] / 5にあります

プログラムは、オブジェクトが占有するストレージを再利用するか、クラス型のオブジェクトのデストラクタを明示的に呼び出すことにより、オブジェクトの寿命を終了させることができます。クラス型のオブジェクトの場合、プログラムは、オブジェクトが占有するストレージが再利用または解放される前に、デストラクタを明示的に呼び出す必要はありません。ただし、デストラクタへの明示的な呼び出しがない場合、またはストレージを解放するために削除式が使用されない場合、デストラクタは暗黙的に呼び出されず、デストラクタによって生成される副作用に依存するプログラムの動作は未定義です。

および[basic.life] / 8

オブジェクトの存続期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトが占有していたストレージの場所に新しいオブジェクトが作成された場合、元のオブジェクトを指すポインター、参照元のオブジェクトを参照した場合、または元のオブジェクトの名前が自動的に新しいオブジェクトを参照し、新しいオブジェクトの存続期間が開始すると、次の場合に新しいオブジェクトを操作できます。

  • 新しいオブジェクトのストレージは、元のオブジェクトが占めていたストレージロケーションを正確にオーバーレイします。

  • 新しいオブジェクトは元のオブジェクトと同じタイプです(トップレベルのcv-qualifiersは無視されます)。

  • 元のオブジェクトの型がconst修飾されておらず、クラス型の場合、型がconst修飾型または参照型の非静的データメンバーを含んでいない。

  • 元のオブジェクトも新しいオブジェクトも、重複する可能性のあるサブオブジェクト([intro.object])ではありません。

オブジェクトは些細なものなので、[basic.life] / 5について心配する必要はなく、[basic.life] / 8の箇条書きの点を満たしていれば安全です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.