none
STL remove_ifの第3引数に付いて RRS feed

  • 質問

  • こんにちはひであきといいます
    VC++と直接関係ありませんが、STLでremove_ifの
    第3引数について教えていただけないでしょうか。
    よろしくお願いいたします。

    struct Hoge {
      int ID;
      /* 以降何らかのデータ */
    }

    /* pred関数のイメージ
    template <typename T>
    bool pred(T obj, int ID) {
      if ( obj.ID == ID ) { return true;}
      return false;
    }
    */
    /* インスタンス作成時にT = Hoge とします。
    template <class T> class CHoge {
      private:
        vector <T> v; //
      public:
        void remove(int ID);
    };

    template <class T> void CHoge <T>::remove(int ID) {
       vector<T>::iterator end
          = remove_if( this->v.begin(), this->v.end(), pred(ID) ); // <--- ここ
       this->v.erase( end, this->v.end() );
    }

    上記ここで、remove_ifの第3引数でpred関数の引数として
    IDを指定して、指定したIDと一致する要素を削除したいのですが、
    predはどのように記述すれば良いのでしょうか?
    なお、IDは必ずユニークになるように別途設定します。
    または、他の方法ありますでしょうか?

     

    2007年3月15日 4:50

回答

  • remove(int ID) とするなら、例えばこんな感じでしょうか。
    --------------------------------------------------
    struct Hoge {
      int ID;
      /* 以降何らかのデータ */
    };

    bool operator ==(const Hoge& lhs, const int rhs)
    {
      return (lhs.ID == rhs);
    }

    template <typename T>
    class IsSameID
    {
    private:
      int _id;

    public:
      IsSameID(int ID) : _id(ID) {}

      bool operator()(T t) const
      {
        return (t == _id);
      }
    };

    /* インスタンス作成時にT = Hoge とします。*/
    template <class T> class CHoge
    {
    private:
      vector <T> v;

    public:
      void remove(int ID);
    };

    template <class T> void CHoge <T>::remove(int ID)
    {
      vector<T>::iterator end = remove_if( this->v.begin(), this->v.end(), IsSameID<T>(ID) );
      this->v.erase( end, this->v.end() );
    }
    --------------------------------------------------


    でも、私ならこっちかな?
    ( remove(T t) とする )
    --------------------------------------------------
    struct Hoge {
      int ID;
      /* 以降何らかのデータ */
    };

    bool operator ==(const Hoge& lhs, const Hoge& rhs)
    {
      return (lhs.ID == rhs.ID);
    }

    template <class T> class CHoge
    {
    private:
      vector <T> v;

    public:
      void remove(T t);
    };

    template <class T> void CHoge<T>::remove(T t)
    {
      vector<T>::iterator end = std::remove( this->v.begin(), this->v.end(), t);
      this->v.erase( end, this->v.end() );
    }

    void removeHoge()
    {
     CHoge<Hoge> hogeObj;

      ......

     Hoge target;
     target.ID = 1;
     hogeObj.remove(target);
    }
    --------------------------------------------------


    ※ちゃんとは試してないので、間違っていたらごめんなさい。

    2007年3月15日 7:23
  • こんばんわ
    教えていただいたコードで無事動きました。
    operatorについてはソースをいじりながら理解を深めたいと
    思います。
    ありがとうございました。
    2007年3月15日 12:10

すべての返信

  • remove(int ID) とするなら、例えばこんな感じでしょうか。
    --------------------------------------------------
    struct Hoge {
      int ID;
      /* 以降何らかのデータ */
    };

    bool operator ==(const Hoge& lhs, const int rhs)
    {
      return (lhs.ID == rhs);
    }

    template <typename T>
    class IsSameID
    {
    private:
      int _id;

    public:
      IsSameID(int ID) : _id(ID) {}

      bool operator()(T t) const
      {
        return (t == _id);
      }
    };

    /* インスタンス作成時にT = Hoge とします。*/
    template <class T> class CHoge
    {
    private:
      vector <T> v;

    public:
      void remove(int ID);
    };

    template <class T> void CHoge <T>::remove(int ID)
    {
      vector<T>::iterator end = remove_if( this->v.begin(), this->v.end(), IsSameID<T>(ID) );
      this->v.erase( end, this->v.end() );
    }
    --------------------------------------------------


    でも、私ならこっちかな?
    ( remove(T t) とする )
    --------------------------------------------------
    struct Hoge {
      int ID;
      /* 以降何らかのデータ */
    };

    bool operator ==(const Hoge& lhs, const Hoge& rhs)
    {
      return (lhs.ID == rhs.ID);
    }

    template <class T> class CHoge
    {
    private:
      vector <T> v;

    public:
      void remove(T t);
    };

    template <class T> void CHoge<T>::remove(T t)
    {
      vector<T>::iterator end = std::remove( this->v.begin(), this->v.end(), t);
      this->v.erase( end, this->v.end() );
    }

    void removeHoge()
    {
     CHoge<Hoge> hogeObj;

      ......

     Hoge target;
     target.ID = 1;
     hogeObj.remove(target);
    }
    --------------------------------------------------


    ※ちゃんとは試してないので、間違っていたらごめんなさい。

    2007年3月15日 7:23
  • 早速ありがとうございます。
    operatorを使うんだろうなあ。。。までは分かったのですが
    具体例が見つけられず困っていました。
    早速参考にさせていただきます。
    ありがとうございました。

    分からなかったので、とりあえず下記コードで代用してました。

     for (vector<T>::iterator it = this->v.begin(); it != this->v.end(); it++) {
       if ( it->ID == ID ) {
         this->v.erase(it);
         break;                              // ここでbreakしないと最後の要素を削除するとエラーになる
       }
    }

    ところで、この==と()を再定義?していますが、
    その範囲はソースのどこまで及ぶのでしょうか?

    p.s.
    remove(T t)はHogeの全ての要素を指定しないといけないので
    めんどくさいかなとやめていました。

    2007年3月15日 7:41
  •  ひであき さんからの引用
    ところで、この==と()を再定義?していますが、
    その範囲はソースのどこまで及ぶのでしょうか?

    class IsSameID の operator() は、あくまでこのクラスのメンバですから、IsSameID クラスインスタンスのスコープです。
    極端な例では下記のような感じで使用できます。

    bool Same(Hoge target)
    {
      IsSameID<Hoge> same(0);
      return same.operator()(target);
    }

    一方、今回のサンプルコードでの

    bool operator ==(const Hoge& lhs, int rhs)
    bool operator ==(const Hoge& lhs, const Hoge& rhs)

    などは、見てのとおりグローバルです。

     ひであき さんからの引用
    remove(T t)はHogeの全ての要素を指定しないといけないので
    めんどくさいかなとやめていました。

    bool operator ==(const Hoge& lhs, const Hoge& rhs)

    の実装では、見てのとおり ID のみの比較しかしていませんから、
    Hoge 構造体の全てのメンバ変数に値をセットする必要はありませんよ。

    2007年3月15日 8:23
  • こんばんわ
    教えていただいたコードで無事動きました。
    operatorについてはソースをいじりながら理解を深めたいと
    思います。
    ありがとうございました。
    2007年3月15日 12:10
  • こんにちは、ひであきです。

    Akira Inoue さんに教えていただいたソースを試しにコンパイルしていましたが
    エラーが出てしまいました。
    なぜこのようなエラーになるのか?教えていただけないでしょうか。
    ちなみに試した環境は都合によりBorland C++です。


    【ソース】
    #include <vector>

    using namespace std;

    struct Hoge {
      int ID;
      /* 以降何らかのデータ */
    };

    template <class T> class CHoge {
     private:
     vector<T> v;

     public:
      bool operator ==(const Hoge& lhs, const Hoge& rhs) { // <--- ここがエラー
       return (lhs.ID == rhs.ID);
      }
      void remove(T t);
    };

    template <class T> void CHoge<T>::remove(T t) {
     vector<T>::iterator end = remove( this->v.begin(), this->v.end(), t);
     this->v.erase( end, this->v.end() );
    }

    void main() {
     CHoge<Hoge> hogeObj;

     Hoge target;
     target.ID = 1;
     hogeObj.remove(target);
    }

    【エラーメッセージ】
     'CHoge<T>::operator ==(const Hoge &,const Hoge &)' は引数 1 つで宣言しなければならない

    2007年3月19日 1:42
  • こんにちは、ひであきです。
    一部自己解決しました。

       friend bool operator ==(const Hoge& lhs, const Hoge& rhs) {
       return (lhs.ID == rhs.ID);
      }
      friend bool operator !=(const Hoge& lhs, const Hoge& rhs) {
       return (lhs.ID != rhs.ID);
      }

    と、friend関数し、「!=」の定義を追加すれば良いようです。
    ただ、こうしなければいけない理由は何なのでしょうか。

    2007年3月19日 4:10
  • friendにしないとメンバ関数だと思っちゃうんでしょね。

    # != の定義、必要ですか? なくていいよに思えるし、VC++8では不要でした。

     

    2007年3月19日 5:33
  • ひであきです
    早速ありがとうございます。

    すぐに試せる環境がBorland C++のため、
    後ほどVC++で確認してみます。
    Borland C++では該当のクラスで!=演算子が
    未定義と怒られました。

    とりあえず解決とします。

    2007年3月19日 6:51
  •  ひであき さんからの引用
    template <class T> class CHoge {
     private:
     vector<T> v;

     public:
      bool operator ==(const Hoge& lhs, const Hoge& rhs) { // <--- ここがエラー
       return (lhs.ID == rhs.ID);
      }
      void remove(T t);
    };

    うーん。私のサンプルをよーく見てみましょう。
    operator == はグローバルなオペレータとしています。

    ひであきさんのコードでは、オペレータをCHogeクラスのメンバに移動していますが、これはちょっと無理があると思います。

    この場合のoperator == は Hoge 構造体の比較のためのものですから、グローバルではなくいずれかのクラスのメンバにしたいのでしたら、以下のように struct Hoge のメンバにするべきでしょう。

    struct Hoge {
      int ID;
      /* 以降何らかのデータ */

      bool operator ==(const Hoge& rhs)
      {
        return (ID == rhs.ID);
      }
    };

    それと Borland C++ では試していないのでわかりませんが、επιστημηさん(師匠)がおっしゃっているとおり、今回のサンプルの場合は operator != は不要と思います。

    2007年3月19日 11:01
  •  επιστημη さんからの引用
    # != の定義、必要ですか? なくていいよに思えるし、VC++8では不要でした。

    ヘッダをごそごそ調べてみました。

     borlandのだと、<algorithm> の std::remove(first, last, val) は削除要素を探すのに
    std::find(first,last,val) を呼んでて、こいつが operator != を使ってます。

    # かたや microsoft のだと、同じく remove が find を呼んでるんだけど、
    # find は operator== を使ってますね。

    2007年3月19日 13:30
  • わざわざありがとうございます。
    コンパイラにより微妙に違ってくるものなのですね。
    大変参考になりました。

    2007年3月20日 1:02
  • グローバルとはスコープがソース単位になるのかと考え、
    クラスのメンバにしたのですが。。。
    そうでもないようですね。。。
    operatorのオーバーロードがまだ理解できていない
    ようです。

    構造体のメンバにするべきですね。
    早速修正してみます。

    ありがとうございました。

    2007年3月20日 1:05
  •  επιστημη さんからの引用
    borlandのだと、<algorithm> の std::remove(first, last, val) は削除要素を探すのに
    std::find(first,last,val) を呼んでて、こいつが operator != を使ってます。

    # かたや microsoft のだと、同じく remove が find を呼んでるんだけど、
    # find は operator== を使ってますね。

    なるほど。

    今まで、std:remove や std:find 系の関数は operator== だけを定義しておけば使えるものと思っていましたが、実装系によっては operator!= も必要になることもあるんですね。

    私も勉強になりました。ありがとうございます。

    2007年3月20日 1:43
  •  ひであき さんからの引用
    構造体のメンバにするべきですね。
    早速修正してみます。

    なんか誤解してんじゃねぇかと。

    class X を == で比較したいなら

    bool X::operator==(const X&) const
    もしくは
    bool operator==(const X&, const X&)
    を定義することになります。
    operator==の対称性( a == b なら b == a )からも、グローバルに定義するのが自然です。

    class X {
    public:
      bool operator==(int n) const; // intと比較できる
    };

    なんてことすると、 x == 3 はいいけど 3 == x でエラーとなります。

     

    2007年3月20日 2:48
  •  chack - Akira Inoue さんからの引用
    今まで、std:remove や std:find 系の関数は operator== だけを定義しておけば使えるものと思っていましたが、実装系によっては operator!= も必要になることもあるんですね。

    言語規格では find(first,last,value) は:
    *i == value であるiを返す( i = [first,last) )
    と定められています。
    そのことからすると" != を必要としない"のが正しいようにも思えます。
    つまりは実装側の不備。

    とはいえ「 == を定義するなら != も定義するが吉」なのは言うまでもないのですが。


     

    2007年3月20日 2:53
  • ご指摘ありがとうございます。
    どうやらグローバルという言葉に勘違いしているようです。
    bool X::operator==(const X&) const
    もしくは
    bool operator==(const X&, const X&)
    とあるように、Xという型を指定している以上
    ==の何でもかんでもが適用されるわけではないんですよね。
    (例えばintの比較など) 
      int i;
      if ( i == 1 ) {}

    2007年3月22日 3:29
  • やってみりゃわかるです。
    2007年3月22日 20:44