一時に関わるオペレータのオーバーロードの解決の順序
-
20-09-2019 - |
質問
以下の最小限の例を考えてみます:
#include <iostream>
using namespace std;
class myostream : public ostream {
public:
myostream(ostream const &other) :
ostream(other.rdbuf())
{ }
};
int main() {
cout << "hello world" << endl;
myostream s(cout);
s << "hello world" << endl;
myostream(cout) << "hello world" << endl;
}
Gで++およびVisual C ++で出力、両方は、ある
hello world
hello world
0x4012a4
一時オブジェクト、myostream(cout)
に書き込みバージョンは、代わりに無料のオペレータostream::operator<<(void *)
の、メンバーのオペレータoperator<<(ostream &, char *)
を好むように見えます。オブジェクトが名前を持っているかどうかの違いを作るようです。
これはなぜ起こるのでしょうか?そして、どのように私は、この動作を防ぐのですか?
編集:それが起こるのはなぜ今、様々な答えからも明らかです。これを防ぐ方法については、次のように魅力的なようです。
class myostream : public ostream {
public:
// ...
myostream &operator<<(char const *str) {
std::operator<<(*this, str);
return *this;
}
};
しかし、これは曖昧さのすべての種類になる。
解決
右辺値は、非const参照にバインドすることはできません。だからあなたの例では、型のostreamの一時的なフリーオペレータの最初の引数にすることはできません<<(STD :: ostreamに&、char型のconst *)とどのような使用されているが、メンバ演算子<<(ボイド*)です。
は、あなたがそれを必要とする場合、あなたは、このような
として呼び出しを追加することができますmyostream(cout).flush() << "foo";
の参照に右辺値を変換するであろう。
C ++ 0X、右辺値参照の導入にオペレータのオーバーロード<<問題の根本的な原因を解決し、パラメータとして右辺値参照を取るを提供できるようになることに注意してください。
他のヒント
は、非const参照に結合することができません。具体的には、最初のパラメータにバインドすることはできません。
operator<<(ostream &, char *)
私は答えのの部分の実現します。一時的には左辺値ではありませんので、型ostream &
の引数として使用することはできません。
質問「どのように私はこの作業を行うことができますが、」残っている...
答えのどれもこれまできれいな溶液を得ているように見えるんので、私は汚い解決策のために解決されます:
myostream operator<<(myostream stream, char const *str) {
std::operator<<(stream, str);
return stream;
}
myostream
は、コピーコンストラクタを持っているので、これはのみ可能です。 (内部的には、REF-カウントstd::stringbuf
によって支えられています。)
、私は、これは事前にC ++ 11の回避策かもしれないと思う。
溶液は、我々は、基本クラスへの非const参照にキャストすることができるメンバー関数<<演算子を有することです
class myostream : public ostream {
public:
// ...
template<typename T>
ostream &operator<<(const T &t) {
//now the first operand is no longer a temporary,
//so the non-member operators will overload correctly
return static_cast<ostream &>(*this) << t;
}
};
まあ、私はこれを原因とC ++の仕様を知らないが、それが起こる理由をSUSSするのは簡単です。
スタック上の一時的な生活は、通常、別の関数に渡される、またはそれに呼ばれる単一の操作を持っています。だから、あなたはそれを自由にオペレーターを呼び出す場合:
演算子<<(myostream(COUT))
これは、この操作の終了時に破壊され、ENDLを追加するための第2の「<<」演算子は、無効なオブジェクトを参照することになります。無料の「<<」オペレータからの戻り値が破壊一時オブジェクトへの参照になります。 C ++の仕様は、おそらくイライラすると混乱C ++プログラマからこのシナリオを防ぐために、無料の演算子についての規則を定義します。
さて、一時的に「<<(ボイド*)」メンバー演算子の場合には、戻り値が破壊されていないスタック上に残っていると、オブジェクト自体、ですので、コンパイラはそれを破壊しないように知っていますしかし、次のメンバ演算子、ENDLをとるものにそれを渡します。一時に連鎖する演算子は、簡潔なC ++コードには便利な機能ですので、私はC ++仕様の設計者は、それを考慮し、意図的にそれをサポートするコンパイラを実装します。
確信しています編集
いくつかは、それが非const参照して行うことであると述べています。このコードはコンパイルされます:
#include <iostream>
using namespace std;
class myostream : public ostream {
public:
myostream(ostream const &other) :
ostream(other.rdbuf())
{ }
~myostream() { cout << " destructing "; }
};
int _tmain(int argc, _TCHAR* argv[])
{
basic_ostream<char>& result = std::operator << (myostream(cout), "This works");
std::operator << (result, "illegal");
return 0;
}
そして、それを返す。
This works destructing illegal