C++のテンプレートクラスでoperatorをオーバーロードする
C++でテンプレートクラスに <<
演算子をオーバーロードしてcout
に出力しようとしたら思ったより大変だったので,メモしておく.
普通のクラスに対して <<
をオーバーロードしようとすると,クラス定義内でfriend宣言してからオーバーロードすることが一般的だと思う.
テンプレートクラスでは事情が違うみたいで,結論から言うと,friend宣言して <<
演算子をオーバーロードすることはできなかった.
やりたいことが書いてあったページ
とりあえず,friendメンバをテンプレート関数として前方宣言しておくとコンパイルが通る.
テンプレート関数の宣言にテンプレートクラスが必要なので,クラスAも宣言する必要がある.
// A.h #include <iostream> template<typename T> class A; template<typename T> std::ostream& operator << (std::ostream &stream, const A<T> &value); template<typename T> class A { public: A(T a); friend std::ostream& operator << <>(std::ostream &stream, const A<T> &value); private: T m_a; }; template<typename T> A<T>::A(T a) : m_a(a) {} template<typename T> std::ostream& operator << (std::ostream &stream, const A<T> &value) { stream << value.m_a; return stream; } // main.cpp #include "A.h" #include <iostream> using namespace std; int main() { A<int> a(1); A<double> b(1.234); cout << a << endl; cout << b << endl; }
実行するとこんな感じ.
$ g++ main.cpp $ ./a.out 1 1.234
どうせ同一ファイル内で同じ定義を書くことになるのに,friend関数だけ個別に宣言しておかないといけないのは非常にめんどくさいし,わかりにくい.
結局上記ブログでも指摘されているように,friend宣言は避けて他の方法でオーバーロードした方がいいのかもしれない.
そもそも,frined関数はクラスの非公開メンバへの直接アクセスが許可されている関数ってだけなので,普通にgetter経由でメンバにアクセスすれば良い.
まあgetterを呼び出すだけのオーバヘッドが気になるんだろうけど…
A.h
だけを以下のように修正した.
#include <iostream> template<typename T> class A { public: A(T a); T getA() const; private: T m_a; }; template<typename T> A<T>::A(T a) : m_a(a) {} template<typename T> T A<T>::getA() const {return m_a;} template<typename T> std::ostream& operator << (std::ostream &stream, const A<T> &value) { stream << value.getA(); return stream; }