web-dev-qa-db-ja.com

メンバー関数としてoperator <<をオーバーロードできません

_operator<<_をメンバー関数としてオーバーロードしようとしています。これを行うだけで機能します。

ヘッダーファイルとMyClass.ccファイルの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);
}
_

この質問 で読んだのですが、メンバー関数にすることはできませんが、理由がわかりませんか?

21
user195488

メンバー関数としてオーバーロードされると、_a << b_はa.operator<<(b)として解釈されるため、1つの明示的なパラメーターのみを取ります(thisを非表示パラメーターとして)。

これには、オーバーロードが左側のオペランドとして使用されるクラスの一部である必要があるため、通常のostreamsなどでは役に立ちません。オーバーロードは、クラスの一部ではなく、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);
}
_

2番目の方法は、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);
}
_

2番目とほぼ同じ3番目の可能性があります。

_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);
    }
};
_

この3番目のケースでは、「名前の挿入」と呼ばれるC++のかなり奇妙な(そしてほとんど知られていない)ルールを使用しています。コンパイラは、friend関数をクラスの一部にすることはできないことを認識しているため、メンバー関数を定義する代わりに、その関数の名前を周囲のスコープ(この場合はグローバルスコープ)に「挿入」します。 )。 _operator<<_はクラス定義内で定義されていますが、それはnotメンバー関数です-グローバル関数です。

49
Jerry Coffin

operator<<をメンバー関数としてオーバーロードできます。ただし、左側にostreamを取り、右側にクラスをとるメンバーoperator<<を作成することはできません。

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

10

次のように考えてください。ostreamにストリーミングするときは、<<演算子ストリームオブジェクト上を呼び出しています。また、ostreamの「private」メソッドを直接変更することは許可されていません。したがって、オーバーロードされたバージョンを作成し、それを友達にする必要があります。

クラスで独自のoperator <<メソッドを作成すると、ostreamオブジェクトではなく、クラスで動作する<<メソッドが作成されます。クラスの内部でostreamを保持してストリーミングすることはできると思いますが、次のような連鎖ステートメントを作成するのは困難です。a << b << c

1
gbjbaanb