web-dev-qa-db-ja.com

ostreamに対して<<演算子を正しくオーバーロードする方法

私は行列演算のためにC++で小さな行列ライブラリを書いています。しかし、私のコンパイラは文句を言っていますが、以前はそうではありませんでした。このコードは6ヶ月間保管され、その間に私は私のコンピューターをdebian etchからlennyにアップグレードしました(g ++(Debian 4.3.2-1.1)4.3.2)しかし、同じg ++のUbuntuシステムでも同じ問題があります。 。

これが私の行列クラスの関連部分です:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
    }
}

そして「実装」:

using namespace Math;

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {

    [...]

}

これはコンパイラによって与えられるエラーです。

matrix.cpp:459:エラー: 'std :: ostream&Math :: Matrix :: operator <<(std :: ostream&、const Math :: Matrix&)'は厳密に1つの引数を取らなければなりません

私はこのエラーに少し混乱していますが、それから私のC++はこの6か月間にたくさんのJavaを実行した後に少し錆びてきました。 :-)

217

関数をfriendとして宣言しました。それはクラスのメンバーではありません。実装からMatrix::を削除する必要があります。 friendは、指定した関数(クラスのメンバーではない)がプライベートメンバー変数にアクセスできることを意味します。この関数を実装した方法は、Matrixクラスのインスタンスメソッドのようなものですが、これは誤りです。

118
Mehrdad Afshari

もう1つの可能性についてお話しします。そのために友人の定義を使用するのが好きです。

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
            [...]
        }
    };
}

関数は周囲の名前空間Mathに自動的にターゲット設定されます(その定義はそのクラスのスコープ内に表示されますが)、Matrixオブジェクトでoperator <<を呼び出して引数依存ルックアップを検索しない限り表示されませんその演算子の定義。 Matrix以外の引数型では見えないため、あいまいな呼び出しに役立つことがあります。その定義を記述するとき、Matrixで定義された名前とMatrix自体を直接参照することもできます。名前を長いプレフィックスで修飾し、Math::Matrix<TypeA, N>などのテンプレートパラメーターを指定する必要はありません。

134

Mehrdadの回答に追加するには、

namespace Math
{
    class Matrix
    {
       public:

       [...]


    }   
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

あなたの実装では

std::ostream& operator<<(std::ostream& stream, 
                     const Math::Matrix& matrix) {
    matrix.print(stream); //assuming you define print for matrix 
    return stream;
 }
74
kal

Matrixクラスを処理するためにoperator <<から派生したすべてのクラスに対してstd::ostreamをオーバーロードすること(そしてMatrixクラスのために<<をオーバーロードしないこと)について話していると仮定すると、ヘッダーのMath名前空間の外側でオーバーロード関数を宣言するほうが理にかなっています。

フレンドリ関数を使用するのは、その機能がパブリックインタフェースを介して実現できない場合に限る。

Matrix.h

namespace Math { 
    class Matrix { 
        //...
    };  
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

演算子overloadは名前空間の外側で宣言されていることに注意してください。

Matrix.cpp

using namespace Math;
using namespace std;

ostream& operator<< (ostream& os, const Matrix& obj) {
    os << obj.getXYZ() << obj.getABC() << '\n';
    return os;
}

一方で、あなたのオーバーロード関数友人になる必要があるなら、すなわち個人的で保護されたメンバーにアクセスする必要があります。

Math.h

namespace Math {
    class Matrix {
        public:
            friend std::ostream& operator<<(std::ostream&, const Matrix&);
    };
}

関数定義を単にusing namespace Math;ではなく名前空間ブロックで囲む必要があります。

Matrix.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}
60
sanjivr

C++ 14では、以下のテンプレートを使用してT :: print(std :: ostream&)constを持つ任意のオブジェクトを印刷できます。メンバー。

template<class T>
auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 
31
QuentinUK