メモ置き場

メモ置き場です.開発したものや調べたことについて書きます.

[tex: ]

C++のテンプレートクラスでoperatorをオーバーロードする

C++でテンプレートクラスに << 演算子オーバーロードしてcoutに出力しようとしたら思ったより大変だったので,メモしておく.
普通のクラスに対して << オーバーロードしようとすると,クラス定義内でfriend宣言してからオーバーロードすることが一般的だと思う.
テンプレートクラスでは事情が違うみたいで,結論から言うと,friend宣言して << 演算子オーバーロードすることはできなかった.

うっかり, やっぱり残しておきます, templateクラスのfriend関数, test1.cppのコンパイル, アドバイスに従って書き換えてみる, 宣言だけ省いてみる, test1.cppのリンクが成功するようにしてみる, temp.. - 忘れたときに備えた記録(2008-01-22)

やりたいことが書いてあったページ


とりあえず,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;
}