C++一時オブジェクトはいつ死ぬのですか?

C++ の多くの操作には、一時的な値が必要です。破壊される前にそれらを使用することが不可欠です。ただし、私が会ったすべての C++ プログラマーが、一時的な有効期限がいつ切れるかをしっかりと理解しているわけではありません。このような状況は、多くの場合、過度に保守的な見積もりにつながりますが、害はありません。それにもかかわらず、プログラマーは、言語が提供しない保証を想定することになり、潜行性のバグにつながります.

このような問題マニフェストの一般的なシナリオの 1 つは、std::string で文字列を操作する場合です。 、ただし、結果の文字列を const char* を取る API にフィードバックします .次のコード スニペットを検討してください:

[[nodiscard]] auto greet(const std::string& name) -> std::string {
    return "Hello, " + name + '!';
}

int main() {
  const std::string name{"Lesley Lai"};
  std::puts(greet(name).c_str());}

C++ 標準は、それが機能することを保証します。標準では、完全な式を評価する最後のステップとして、すべての一時オブジェクトを破棄することが義務付けられています 一時ファイルが作成された場所を含む1 . 「完全な式」とは、他の式の部分式ではない式を意味します。

ルールの注目すべき例外は参照です。参照は一時的なものの寿命を延ばすことができますが、それらは別の投稿のトピックになります.

「The Design and Evolution of C++」で、Bjarne は一時オブジェクトの有効期間に関する初期の設計上の決定について説明しました2 。 .この本は、いくつかの代替破壊ポイントを特定した以前の論文を参照しています。たとえば、C++ の元の CFront 実装では、一時オブジェクトはブロックの最後で破棄されます。このアプローチは、大規模な一時ファイルを作成するときに問題を引き起こし、一部のプログラマーは、ステートメントを中かっこで囲むことによって、この問題を明示的に回避しました。

別のアプローチは、最初の使用後に一時的に削除することです。過度に妄想的な C++ プログラマーは、上記のコードが未定義の動作につながるのではないかと疑うかもしれません。この考えを持つことは、このアプローチの無意識の仮定を意味するかもしれません.この戦略は一貫性があるため、現在の方法よりも直感的だと思います。現在の戦略では、上記のコードを少し変更すると、未定義の動作が導入されます:

[[nodiscard]] auto greet(const std::string& name) -> std::string {
    return "Hello, " + name + '!';
}

int main() {
  std::string name{"Lesley Lai"};
  const char* greeting = greet(name).c_str();  std::puts(greeting);}

上記のコードでは、greet によって返されたテンポラリのデストラクタ 完全な式を評価した後に呼び出されます。したがって、ポインター greeting ぶら下がります。以前、OpenGL シェーダーを扱っていたときに、期限切れの一時文字列に噛まれました。そのようなコードが表示されることさえあります 場合によっては動作する 3 小さな文字列の最適化のため .とにかく、未定義の動作が保証された場合 ある方法でプログラムを壊すために、それはもはや未定義ではありません.

C++ が現在の方法を選択する理由まず、C++ はガベージ コレクション ランタイムを提供できないため、「最後の使用後」は想定外です。また、現在のアプローチは、「最初の使用後」の戦略よりも初心者にとってエラーが発生しにくい一方で、「ブロックの最後」が意味する奇妙な回避策がなくてもパフォーマンスが向上します。

<オール>
  • cppreference:ライフタイム↩
  • D&E、6.3.2↩
  • たとえば、単体テスト↩