1

次のコードがあります。

//update it in the map
      std::map<std::string, std::string>::iterator it;
      for(it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end(); ++it)
      {
        if(it->first == change.first)
        {
          if(change.second == "")
          {
            spreadsheets.at(i).cells.erase(change.first);
          }
          else
          {
          it->second = change.second;
          }
        }
      }

上記のコードは私の Macでは完全に動作しますが、Linux コンピューターで実行すると、セグ フォールトがスローされます。spreadsheets.at(i).cells.erase(change.first);

このエラーの原因は何ですか? に変更しようとしerase(change.first)ましたerase(it)が、まだセグメント障害が発生しています。

4

6 に答える 6

4

コンテナから消去すると、イテレータは無効になりますが、ループは継続します。

ループを次のように変更できます。

std::map<std::string, std::string>::iterator it = spreadsheets.at(i).cells.begin();
while (it != spreadsheets.at(i).cells.end())
{
  if(it->first == change.first)
  {
    if(change.second == "")
    {
      spreadsheets.at(i).cells.erase(it++);  //Post increment returns a copy pointing at the current element, while it already points to the next element and thus stays valid after erase
    }
    else
    {
      it->second = change.second;
      ++it;
    }
  }
  else
    ++it;
}

考えてみると、イテレータが指しているペアの最初の要素で消去するのはなぜですか。

spreadsheets.at(i).cells.erase(change.first);

それ以外の

spreadsheets.at(i).cells.erase(it);

別のルックアップを行う必要があるため、効率が低下します。

于 2013-04-23T20:51:57.513 に答える
4

のドキュメントからstd::map::erase:

消去された要素への参照と反復子は無効になります。他の参照と反復子は影響を受けません。

それでもループが続き、(現在は無効な) イテレータをインクリメントします。

修正: イテレータを別の方法でインクリメントします。例:

std::map<std::string, std::string>::iterator it;
for (it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end();/*NOTE: no increment here*/)
{
  if (it->first == change.first)
  {
    if (change.second == "")
    {
      it = spreadsheets.at(i).cells.erase(it); // C++11 only
      // in both C++03 and C++11:  spreadsheets.at(i).cells.erase(it++);
    }
    else
    {
      it->second = change.second;
      ++it;
    }
  }
  else
    ++it;
}

または、多数の実行パスによる混乱を避けるために (最初の試行で最後を忘れたのとまったく同じ混乱else): イテレータをコピーし、元のパスをインクリメントしてから、コピーを使用します。あなたの場合、これはやり過ぎに見えるかもしれませんが、より複雑なループの場合、これが正気を保つ唯一の方法である場合があります。;)

std::map<std::string, std::string>::iterator it;
for (it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end();/*NOTE: no increment here*/)
{
  std::map<std::string, std::string>::iterator this_it = it++;
  if (this_it->first == change.first)
  {
    if (change.second == "")
    {
      spreadsheets.at(i).cells.erase(this_it);
    }
    else
    {
      this_it->second = change.second;
    }
  }
}
于 2013-04-23T20:54:52.933 に答える
2

この要素を指しているマップから要素が削除された後、無効になります。したがって、spreadsheets.at(i).cells.erase(change.first);無効itになります。イテレータの無効化規則を参照してください

于 2013-04-23T20:53:20.247 に答える
1

消去された要素への参照と反復子は無効になります。

//update it in the map
std::map<std::string, std::string>::iterator it;
for(it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end(); ++it)
{
    if(it->first == change.first)
    {
        if(change.second == "")
        {
            spreadsheets.at(i).cells.erase(it--);
        }
        else
        {
            it->second = change.second;
        }
    }
}
于 2015-09-02T09:47:38.163 に答える
0

現時点spreadsheets.at(i).cells.erase(change.first);では、std::map (現在の change.first キー) でイテレータを実行すると無効になります。したがって、実行するとit++、未定義の動作になります。

標準コンテナー内の反復子の無効化に関する規則については、反復子無効化の規則を参照してください。

于 2013-04-23T20:52:24.827 に答える
0

消去する前に反復子をインクリメントします。なぜMacでは起こらなかったのですか?誰が知っている..異なるOS、異なる動作。

それで

于 2013-04-23T20:56:40.653 に答える