2016-10-14 18 views
0

を逆multi_index I以下(簡体字)のコードを持っている:ブーストは、イテレータの消去トラブル

#include <boost/multi_index_container.hpp> 
#include <boost/multi_index/ordered_index.hpp> 
namespace bmi = boost::multi_index; 

#include <string> 
#include <iostream> 
#include <cassert> 

using Container = boost::multi_index_container< 
    std::string, 
    bmi::indexed_by< bmi::ordered_non_unique< bmi::identity<std::string> > > 
>; 

/// Get the base of a non-reverse iterator. It's the iterator itself. 
inline 
Container::iterator const& 
iter_base(Container::iterator const& it) 
{ 
    return it; 
} 

/** Get a non-reverse iterator that points at the same element as the given reverse_iterator. 
* 
* @param rit reverse_iterator 
* @return a (non-reverse) iterator that points to the same element. 
* @pre @p rit is dereferenceable (not equal to @c rend() of whatever container @p rit came from) 
*/ 
inline 
Container::iterator 
iter_base(Container::reverse_iterator const& rit) 
{ 
    auto bit = rit.base(); 
    // if 'rit' is a reverse iterator: &*(rit.base() - 1) == &*rit 
    return --bit; 
} 

template <typename IT> 
void evict(Container& c, IT rb, IT fin) 
{ 
    std::vector<std::string> result; 
    for (; rb != fin;) { 
     if (rb->size() == 3) { 
      auto victim = rb; 
      ++rb; 
      std::cout << "victim->" << *victim << ", next->" << (rb==fin ? std::string{"THE END"} : *rb) << "\n"; 
      auto next = c.erase(iter_base(victim)); 
      std::cout << "size=" << c.size() << "\n"; 
      for (auto const& s : c) { 
       std::cout << "remain: " << s << "\n"; // bar - baz - foo 
      } 

      rb = IT(next); 
      (void)next; 
     } 
     else { 
      result.push_back(*rb); 
     } 
    } 
} 

int main(int argc, char**) 
{ 
    bool forward = (argc == 1); 

    Container c; 
    c.insert("foo"); // will be last 
    c.insert("bar"); 
    c.insert("baz"); 

    if (forward) { 
     auto b = c.lower_bound("baz"); 

     std::cout << ">> " << *b << "\n"; // prints baz 

     auto rb = (b); 
     std::cout << "<< " << *rb   << "\n"; // prints baz 
     std::cout << "<< " << *iter_base(rb) << "\n"; // prints baz 

     evict(c, rb, c.end()); 
    } 
    else { 
     auto b = c.upper_bound("baz"); 

     std::cout << ">> " << *b << "\n"; // prints foo 

     auto rb = Container::reverse_iterator(b); 
     std::cout << "<< " << *rb   << "\n"; // prints baz 
     std::cout << "<< " << *iter_base(rb) << "\n"; // prints baz 

     evict(c, rb, c.rend()); 
    } 
} 

実際のコードは、単に消去だけではありませんが、これは動作を説明するのに十分です。

ループ内で単なる削除が行われないことを示すためにEDITEDが表示されます。 アイテムは、使用されたイテレータの種類に応じて、順方向または逆順にresultに追加されるものとします。

引数なしで実行すると、forward==true、出力が期待通りである:引数を指定して実行すると、

>> baz 
<< baz 
<< baz 
victim->baz, next->foo 
size=2 
remain: bar 
remain: foo 
victim->foo, next->THE END 
size=1 
remain: bar 

forward==false、出力は次のとおりです。

>> foo 
<< baz 
<< baz 
victim->baz, next->bar 
size=2 
remain: bar 
remain: foo 
segmentation fault (core dumped) 

(期待できないとして)

アドレスサニタイザでコンパイルすると、42行目のヒープ使用後の状態(++ rb行)が示されます。

erase(victim)を呼び出すと、消去が他のイテレータを無効にするものではないにもかかわらず、何とかrbが無効になっているようです。

私が間違っていることを知っていますか?

答えて

2

第二に答えを、横断はイテレータの性質に応じて、直接または逆の順序で行うことがOPからの追加の要求を次のようにそれを行うための一つの可能​​性のある方法です。これは次のように行うことができることはほとんど注意を払って:

template <typename IT> 
void evict(Container& c, IT rb, IT fin) 
{ 
    std::vector<std::string> result; 
    if(rb != fin) for(;;) { 
     IT next = rb; 
     ++next; 
     bool finished = (next == fin); 
     if (rb->size() == 3) { 
      c.erase(iter_base(rb)); 
      std::cout << "size=" << c.size() << "\n"; 
      for (auto const& s : c) { 
       std::cout << "remain: " << s << "\n"; // bar - baz - foo 
      } 
     } 
     else { 
      result.push_back(*rb); 
     } 
     if(finished) break; 
     rb = next; 
    } 
} 

私の悪い、コードを通じて被災地はまだUBに走っていました。これを試してください:

template <typename IT> 
void evict(Container& c, IT rb, IT fin) 
{ 
    std::vector<std::string> result; 
    if(rb != fin) for(;;) { 
     bool finished = (std::next(rb) == fin); 
     if (rb->size() == 3) { 
      rb = IT{c.erase(iter_base(rb))}; 
      std::cout << "size=" << c.size() << "\n"; 
      for (auto const& s : c) { 
       std::cout << "remain: " << s << "\n"; // bar - baz - foo 
      } 

     } 
     else { 
      result.push_back(*rb); 
     } 
     if(finished) break; 
    } 
} 
+0

残念ながら、これは役に立ちません。 heap-after-after freeは '++ next'行に残っています。 – Bulletmagnet

+0

提案コード*をそのままコピーしましたか?例えばif(rb!= fin)for(;;)の部分に注意してください。 –

+0

申し訳ありませんが、提案された解決策は本当に間違っていました。うまくいけば正しい代替方法で編集しました。 –

2

いいえ、リバースイテレータを扱うことは首に痛みです。さんはevictのコードのこの部分の実行中にポインタのビジネスを分析してみましょう:

auto victim = rb; 
++rb; 
auto next = c.erase(iter_base(victim)); 

evict(c, Container::reverse_iterator(c.upper_bound("baz")), c.rend())への呼び出しの内部。 「ポイントする」とは、「内部イテレータが指し示す」という意味です。ステップバイステップは、我々は持っている:

  1. コードを入力する前に:rbポイントを"foo"に、victimはまだ存在していません。

    auto victim = rb;

  2. rb"foo"から"foo"victimポイントに。

    ++rb;

  3. rb"foo"から"baz"victimポイントに。

    auto next = c.erase(iter_base(victim));

  4. "baz"が消去され、rbポイントが"foo""baz"victimポイントを削除しました。 rbを使用した更なる参照、比較、(デイン/インチ)クリメーティング操作は未定義の動作です。

私はあなたがイテレータと逆イテレータの両方で動作するevict関数を記述しようとしている理解しています。

template<typename Container> 
std::pair<typename Container::iterator,typename Container::iterator> 
direct_range(
    typename Container::iterator first, 
    typename Container::iterator last) 
{ 
    return {first,last}; 
} 

template<typename Container> 
std::pair<typename Container::iterator,typename Container::iterator> 
direct_range(
    typename Container::reverse_iterator first, 
    typename Container::reverse_iterator last) 
{ 
    return {last.base(),first.base()}; 
} 

template <typename IT> 
void evict(Container& c, IT rb, IT fin) 
{ 
    auto p=direct_range<Container>(rb,fin); 
    c.erase(p.first,p.second); 

    for(auto const& s:c){ 
    std::cout<<"remain: "<<s<<"\n"; // bar - baz - foo 
    } 
} 
+0

ありがとうございました。残念なことに、実際のコードは 'erase(iterator、iterator)'を使うのに十分単純ではありません(あなたが望むなら、いくつかの要素だけが消去されると想像してください)。 – Bulletmagnet

+0

あなたは「4. baz is erased」と書いています。 'iter_base(victim)' - イテレータ - が 'rb'と同じ要素を指しているのは逆のイテレータなのですから? – Bulletmagnet

+0

あなたの最初の返事に: 'direct_range'の結果が得られれば、' erase(iterator、iterator) 'を非リバースイテレータで動作するより精巧なコードに置き換えることができます。 –

関連する問題