ゆとりーなの日記

日記的な事を書いて行くと思はれる

自動生成の時間

Boost.operatorsを使うと+=とか==を定義しておくだけで、+やら!=が使えるようになるという便利な代物があるわけです。
で、例えばこんなクラスがあるとします。

class vector2 : boost::operators<vector2> {
 public:
  explicit vector2(const float x, const float y) : x_(x), y_(y) {}
  
  float x() const {
  	return x_;
  }
  
  float y() const {
  	return y_;
  }
  
  void set_x(const float x) {
  	x_ = x;
  }
  
  void set_y(const float y) {
  	y_ = y;
  }
  
  vector2 &operator +=(const vector2 &rhs) {
  	x_ += rhs.x_;
  	y_ += rhs.y_;
    return *this;
  }

  vector2 &operator -=(const vector2 &rhs) {
    x_ -= rhs.x_;
    y_ -= rhs.y_;
    return *this;
  }

  vector2 &operator *=(const float rhs) {
    x_ *= rhs;
    y_ *= rhs;
    return *this;
  }

  vector2 &operator /=(const float rhs) {
    x_ /= rhs;
    y_ /= rhs;
    return *this;
  }
   
 private:
  float x_;
  float y_;
};

bool operator ==(const vector2 &lhs, const vector2 &rhs) {
  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}

こいつはoperator *の部分でコンパイルエラーなわけです。 boost::operatorsを継承しただけだと、自身を取る演算子しか自動生成してくれないみたいです。
で、テンプレート引数にどうも第二引数があるみたいなので、これを使えばいけるのかとかおもったのですが、

class vector2 : boost::operators<vector2, float> {
 public:
  explicit vector2(const float x, const float y) : x_(x), y_(y) {}
  
  float x() const {
  	return x_;
  }
  
  float y() const {
  	return y_;
  }
  
  void set_x(const float x) {
  	x_ = x;
  }
  
  void set_y(const float y) {
  	y_ = y;
  }
  
  vector2 &operator +=(const vector2 &rhs) {
  	x_ += rhs.x_;
  	y_ += rhs.y_;
    return *this;
  }

  vector2 &operator -=(const vector2 &rhs) {
    x_ -= rhs.x_;
    y_ -= rhs.y_;
    return *this;
  }

  vector2 &operator *=(const float rhs) {
    x_ *= rhs;
    y_ *= rhs;
    return *this;
  }

  vector2 &operator /=(const float rhs) {
    x_ /= rhs;
    y_ /= rhs;
    return *this;
  }
   
 private:
  float x_;
  float y_;
};

bool operator ==(const vector2 &lhs, const vector2 &rhs) {
  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}

こんどはoperator !=の方が残当しました。第二引数を指定してしまうとどうも今度はその型を取るのしか自動生成してくれないのですね。
というわけで強欲に両方行くのが正解のようです。

class vector2 : boost::operators<vector2>,
                     boost::operators<vector2, float> {
 public:
  explicit vector2(const float x, const float y) : x_(x), y_(y) {}
  
  float x() const {
  	return x_;
  }
  
  float y() const {
  	return y_;
  }
  
  void set_x(const float x) {
  	x_ = x;
  }
  
  void set_y(const float y) {
  	y_ = y;
  }
  
  vector2 &operator +=(const vector2 &rhs) {
  	x_ += rhs.x_;
  	y_ += rhs.y_;
    return *this;
  }

  vector2 &operator -=(const vector2 &rhs) {
    x_ -= rhs.x_;
    y_ -= rhs.y_;
    return *this;
  }

  vector2 &operator *=(const float rhs) {
    x_ *= rhs;
    y_ *= rhs;
    return *this;
  }

  vector2 &operator /=(const float rhs) {
    x_ /= rhs;
    y_ /= rhs;
    return *this;
  }
   
 private:
  float x_;
  float y_;
};

bool operator ==(const vector2 &lhs, const vector2 &rhs) {
  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}

ただこれ、強欲過ぎないかという疑問があるわけです。というのも細かく指定して自動生成させることもできるからですからね。

class vector2 : boost::equality_comparable<vector2>,
                     boost::addable<vector2>,
                     boost::subtractable<vector2>,
                     boost::dividable2<vector2, float>,
                     boost::multipliable2<vector2, float> {
 public:
  explicit vector2(const float x, const float y) : x_(x), y_(y) {}
  
  float x() const {
  	return x_;
  }
  
  float y() const {
  	return y_;
  }
  
  void set_x(const float x) {
  	x_ = x;
  }
  
  void set_y(const float y) {
  	y_ = y;
  }
  
  vector2 &operator +=(const vector2 &rhs) {
  	x_ += rhs.x_;
  	y_ += rhs.y_;
    return *this;
  }

  vector2 &operator -=(const vector2 &rhs) {
    x_ -= rhs.x_;
    y_ -= rhs.y_;
    return *this;
  }

  vector2 &operator *=(const float rhs) {
    x_ *= rhs;
    y_ *= rhs;
    return *this;
  }

  vector2 &operator /=(const float rhs) {
    x_ /= rhs;
    y_ /= rhs;
    return *this;
  }
   
 private:
  float x_;
  float y_;
};

bool operator ==(const vector2 &lhs, const vector2 &rhs) {
  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}

こっちの方が無駄なものがなくてお行儀が良さそうな気がしなくもないです。
あと最初気づかなかったのですが、Boost.operatorsさんはvector2 * floatだけではなくfloat * vector2も自動で生成してくれるみたいでおいしいですね。