ループのベースの悪名高いバグ


あなたが恐れなしでお好みの言語の機能を使用して想像してください.あなたが無限にその機能を使用して、第二の考えを持たずに想像してください.あなたも会話の中で、コードレビューでは、協議では、どこでもそれを推進した.
エラーが起こりがちで、未定義の動作につながる可能性があります.かろうじて想像できるコーナーケースだけでなく、完全に通常のシナリオで.
それはあなたに起こったことがありますか?
それは確かに私に起こった.
私は、数週間前に、レンジベースのfor ループが壊れている.

何が問題ですか。


簡単に言えば、一時的な値への参照を繰り返すことは未定義です.
具体例を見てみましょう.
#include <iostream>
#include <string>
#include <vector>

std::vector<std::string> createStrings() {
    return {"This", "is", "a", "vector", "of", "strings"};
}

int main()
{
  for (auto w: createStrings()) {
      std::cout << w << " "; // this works fine
  }
  std::cout << std::endl;
  for (auto c: createStrings()[0]) {
      std::cout << c << " "; // this is UB
  }
  std::cout << std::endl;
}
次のコードを実行すると、ループの最初の部分がうまく動作することがわかりますが、2つめはいくつかのゴミを出力します.
この例では、文字列のベクトルの要素を取得することで演奏しましたが、タプルの要素を取得しようとした場合や、オプションのベクトルの要素を反復したい場合は同じ問題になります.
#include <iostream>
#include <optional>
#include <string>
#include <vector>

std::vector<std::string> createStrings() {
    return {"This", "is", "a", "vector", "of", "strings"};
}

std::optional<std::vector<int>> createOptionalInts() {
    return std::optional<std::vector<int>>{{1, 2, 3, 4}};
}


int main()
{
  for (auto i: createOptionalInts().value()) {
      std::cout << i << " "; // UB
  }
  std::cout << std::endl;
}
/*
In my environment, the output happened to be
0 0 3 4
*/
これはかなり深刻な問題であり、実際にこの問題に遭遇することができます.
この行動の根本原因を理解するためには、どのようにしてfor ループを実装します.
標準に従って、そのようなループはいくつかの声明に拡大されます.本質的に、彼らは良い古いfor 開始および終了イテレータが外部で宣言されているループ
#include <iostream>
#include <optional>
#include <string>
#include <vector>

std::optional<std::vector<int>> createOptionalInts() {
    return std::optional<std::vector<int>>{{1, 2, 3, 4}};
}

int main()
{  
  auto&& range = createOptionalInts().value();
  auto position = range.begin();
  auto end = range.end();
  for(; position != end; ++position) {
      std::cout << *(position) << " "; // UB
  }
  std::cout << std::endl; 
}
それを再生することができますC++Insights
言語の規則によると、一時的な値はrange , それに直接バインドされていないfor ループ開始.

何ができる?


まず第一に、あなたは問題について学び、他の人とそれを共有しなければなりません.初心者の場合は、特定の状況での制約とリスクがあると述べ、それらの状況を高いレベルで記述することができます.
我々はすべてのルールを認識していない限り、これは明らかに明白な問題からです.
したがって、より経験豊富なプログラマのためにも、特にどのように範囲ベースの詳細を教えてくださいfor ループが拡張されます.それは前のセクションで簡単に見たものですP2012R0 明確にその正確な詳細に役立ちます.
教育は今できることです.書籍やスタイルガイドなどの問題に言及しているEmbracing Modern C++ Safely and Abseil Tip #107 , しかし、我々は誰もが問題について知っているいくつかのソースに基づいてそれを期待することはできません.
我々は、メッセージを通過する必要がありますfor ループは一時的なオブジェクトへの参照を繰り返すときには動きません.

それは固定されますか。


この問題は解決されるでしょうか?あなたはこの点を尋ねるべきです.たぶん、はい、確かに当分の間.私は、このため、問題について学びました.私は、修正がちょうどC++ Evolutionワーキンググループ(EWG)によって拒絶されたと言及しました.
提案P2012R0 ニコjusuttis、ビクターZverovich、フィリピンMolundeとアーサーO ' dwyerによって書かれていた委員会でうまく進んでいたが、最終的にそれは十分にジェネリックであると判断されなかったため、言語にそれをしなかった.
彼らはループの拡張によって問題を解決することを提案した.最後の目標は、ユニバーサルリファレンスの寿命を延長することであるfor ループ.アイデアは、新しい生涯のルールを導入せずにこれを達成することでした.
提案が拒否されたので、我々は次のバージョンでこれを固定することを期待することができません、しかし、うまくいけば、コミュニティは多分C++ 26のために解決を見つけるでしょう.

結論


この記事では、私はあなたとあなたと共有していました.範囲ベースfor ループが壊れている.一時的な値への参照を扱うことができないので、彼らは未定義のふるまいの温床です.
これは本当の問題です、我々は異なる現実的な使用事例を見ました、そして、それは長い間知られていました.それは私たちのコミュニティからの著名な人格によって書かれた修正する提案があったが、それは受け入れられませんでした-解決策が十分に包括的でない限り.
あなたはこの問題に遭遇したことがありますか.

接続深い


この記事が好きなら
  • ボタンのようにヒット

  • subscribe to my newsletter
  • そして、接続しましょう!