演算子オーバーロードとfriend関数
行列の計算を今までEigenにやらせてたんだけど、やっぱり自分で作るかーと思ったのでここ数日行列クラスを実装する作業に明け暮れてた。
こんな感じ。
// TEMPLATE CLASS Matrix template <class Ty_> class Matrix { // define my matrix class std::vector<std::vector<Ty_>> mat_; public: explicit Matrix(const int rows = 0, const int cols = 0) : mat_(rows, std::vector<Ty_>(cols, 0)) { // construct from parameters } Matrix(const std::initializer_list<std::initializer_list<Ty_>> right) { // construct from initializer for (auto iter = right.begin(); iter < right.end(); iter++) this->mat_.push_back(*iter); } int rows(void) const { // get row size return this->mat_.size(); } // ...... }
これがすごい勉強になる。
やっぱり汎用的なライブラリを1から作るのは大変だけど、色々テクニックを学べるから、本気でC++やるなら一度はやってみるといいと思う。
特にfriend関数とかは普通にコーディングしてるだけではなかなか出会えないけど、ここに来て多用せざるを得なくなり、改めて勉強することになった。
というわけで今日は演算子オーバーロードとfriend関数ついて。
演算子オーバーロード
こういう算術系のクラスを作る場合、コードの半分ぐらいは演算子オーバーロードで埋め尽くされる。
例えば任意の型の変数を1つ保持し、それについての処理を記述するScalarクラスがあったとする。
これにScalar+変数の演算をオーバーロードする。
template <class Ty_> class Scalar { Ty_ var_; public: Scalar(const Ty_ var) : var_(var) {} Ty_ operator+(const Ty_ right) const { return var_ + right; } };
こんな感じで、object+何かという演算をしたい場合は、operator+をオーバーロードして、引数に何かの方を与えればよい。
しかし、このやり方では何か+objectという演算を定義できない。
そもそもメンバ関数による演算子オーバーロードは必ず左辺に自身のオブジェクトがくる必要があるため、メンバ変数としてこれを実装することはできない。
方法として、クラスの外に
template <class Ty_> Ty_ operator+(const Ty_ left, Scalar<Ty_> right);
を定義するというのが最初に思い浮かぶけど、var_は非公開メンバであるため、この関数内でvar_を直接参照することができず、別途var_のゲッタ等を準備する必要がある。
というわけで察しはつくだろうけど、こういう時にfriendを使いましょうという話。
template <class Ty_> class Scalar { Ty_ var_; public: Scalar(const Ty_ var) : var_(var) {} Ty_ operator+(const Ty_ right) const { return var_ + right; } friend Ty_ operator+(const Ty_ left, const Scalar<Ty_> right) { return left + right.var_; } };
これでOK。
ちなみにfriendはその性質上、宣言だけクラス内でして実装はクラス外でやるのが普通だけど、
クラスとの論理的な関わりの強い演算子オーバーロードの場合、見た目にも綺麗だし、inline化もされるから実装もクラス内でやったほうが良さそう。