2012-03-21 15 views
10

メンバー関数としてoperator<<をオーバーロードしようとしています。私のヘッダファイルに、私のMyClass.ccファイルにoperator <<をメンバー関数としてオーバーロードできません

friend ostream& operator<<(ostream& os, const MyClass& myClass);:単純にこれを行う場合、それは動作します

ostream& operator<<(ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

をしかし、私はfriendオフを取り、それをメンバ関数を作成しようとした場合、それoperator<<は1つの議論しか取ることができないと訴える。どうして?

ostream& MyClass::operator<<(ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

私はそれがメンバ関数ではないことをthis questionで読まが、なぜわかりませんか?

答えて

35

メンバー関数としてオーバーロードされた場合、a << ba.operator<<(b)と解釈されるため、1つの明示的なパラメータ(隠しパラメータとしてthis)のみが使用されます。

これは、オーバーロードが左側のオペランドとして使用されるクラスの一部であることを必要とするため、普通のostreamなどでは役に立ちません。オーバーロードはクラスの一部ではなく、ostreamクラスの一部である必要があります。 ostreamを変更することは許可されていないので、そのことはできません。それは代替としてグローバルオーバーロードだけを残します。

があり、しかし、あなたはグローバル演算子をオーバーロードが、それはメンバ関数を呼び出す必要があり、かなり広く使用されているパターン:

class whatever { 
    // make this public, or the global overload a friend. 
    std::ostream &write(std::ostream &dest) const { 
     // write self to dest 
    } 
}; 

std::ostream &operator<<(std::ostream &os, whatever const &w) { 
    return w.write(os); 
} 

あなたはポリモーフィック行動をしたい場合はとき/これは特に便利です。オーバーロードされた演算子を多形体にすることはできませんが、それはvirtualというメンバ関数を作成するので、とにかに多態的に動作します。

編集:(私が望む)状況を明確にするには、これをいくつかの方法で行うことができます。最初の、おそらく最も明白なのは、私たちのwriteメンバーをパブリックにし、グローバルオペレータにそれを呼び出させることです。それが公開されているので、我々は、オペレータがそれを使用できるように特別な何かをする必要はありません。

class myClass { 
public: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 
}; 

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
    // since `write` is public, we can call it without any problem. 
    return m.write(os); 
} 

第二の代替はwriteをプライベートに、それにアクセスを与えるためにoperator<<友人を宣言することです

class myClass { 
    // Note this is private: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 

    // since `write` is private, we declare `operator<<` a friend to give it access: 
    friend std::ostream &operator<<(std::ostream &, myClass const &); 
}; 

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
    return m.write(os); 
} 

ほぼ第二のようなものだ第三の可能性があります:

class myClass { 
    // Note this is private: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 

    // since `write` is private, we declare `operator<<` a friend to give it access. 
    // We also implement it right here inside the class definition though: 
    friend std::ostream &operator<<(std::ostream &os, myClas const &m) { 
     return m.write(os); 
    } 
}; 

この第三の場合は、C +にかなり奇妙(及び少し知られている)ルールを使用して+ "名前インジェクション"と呼ばれる。コンパイラはfriend関数をクラスの一部にすることはできないことを知っているので、メンバ関数を定義する代わりに、その関数の名前を周囲のスコープ(この場合はグローバルスコープ)に "注入"します。クラス定義内にoperator<<が定義されていても、それはではありません。メンバー関数はまったくありません。これはグローバル関数です。

+0

'operator <<'がクラス内でpublicとして宣言されていて、クラス内で 'MyClass :: operator <<'として実装されていない場合はどうなりますか? 「友人」は必要ですか? –

+0

興味深い。クラス内で宣言され、非メンバ関数として実装され、そのクラスのpublicメンバ関数を呼び出すクラスの友人ではありません。 –

+0

@ 0A0D:私は私がついてるかどうかはわかりません。私の前のコメントでは、 "it"はメンバ関数を参照していました。あなたは何か他のことについて話していますか? –

9

メンバ関数としてoperator<<をオーバーロードすることができます。しかし、左側にostream、右側にクラスを取るメンバーoperator<<を書くことはできません。

(静的ではない)メンバ関数を作成するとき、暗黙の最初の引数、呼び出し元オブジェクトがあります。 operator<<はバイナリなので、引数は2つしかかかりません。それをメンバ関数にすると、すでに1つのパラメータ(呼び出し元のオブジェクト)があるため、1つのパラメータを与えることができます。その呼び出し元のオブジェクトは常に最初の引数であるため、出力演算子を(少なくともその特定の関数の標準形式ではない)(静的でない)メンバとして書くことはできません。その場合、ostream最初の議論である必要があります。

+1

また、暗黙の 'this'が最初の引数になるため、ostream演算子をクラスメンバーとして作成することはできません。引数の順序は逆になります。 –

+0

@JohnZwinckあなたのクラスを 'myclass >> cout'にすることはできますが、それは変です...特に連鎖を使うと。 –

+0

メンバ関数でない場合は、MyClassの印刷メソッドにアクセスする必要がありますか? –

1

このようなことを考える:あなたはのostreamにストリーミングしたい場合、あなたはストリームオブジェクトに< <オペレータを呼んでいます。また、ostreamの「プライベート」メソッドを直接変更することはできません。したがって、オーバーロードされたバージョンを作成して、それを友人にする必要があります。

クラスに独自の演算子< <を作成すると、ostreamオブジェクトではなくクラスで動作する< <メソッドが作成されます。あなたのクラスに内部的にストリームを流してストリームすることができますが、a << b << cのような連鎖文を書くのは難しいでしょう。

+0

私の印刷メソッドがMyClassで公開されていても、友人は必要ですか? –

関連する問題