11

次の最小限の例を考えてみましょう。

#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++とVisualC++の両方での出力は次のとおりです。

hello world
hello world
0x4012a4

一時オブジェクトに書き込むバージョンは、free演算子ではなくmyostream(cout)、member演算子を優先するように見えます。オブジェクトに名前があるかどうかに違いがあるようです。ostream::operator<<(void *)operator<<(ostream &, char *)

なぜこれが起こるのですか?そして、どうすればこの動作を防ぐことができますか?

編集:なぜそれが起こるのかは、さまざまな答えから明らかになりました。これを防ぐ方法については、次のことが魅力的です。

class myostream : public ostream {
    public:
        // ...
        myostream &operator<<(char const *str) {
            std::operator<<(*this, str);
            return *this;
        }
};

ただし、これにより、あらゆる種類のあいまいさが生じます。

4

6 に答える 6

7

オブジェクトに名前がない場合 (つまり、一時オブジェクトである場合)、非 const 参照にバインドすることはできません。具体的には、次の最初のパラメーターにバインドすることはできません。

operator<<(ostream &, char *)
于 2010-02-11T16:16:00.833 に答える
6

右辺値は非 const 参照にバインドできません。したがって、あなたの例では、タイプ ostream の一時的なものは free operator<<(std::ostream&, char const*) の最初の引数にすることはできず、使用されるのはメンバー operator<<(void*) です。

必要な場合は、次のような呼び出しを追加できます

myostream(cout).flush() << "foo";

これにより、右辺値が参照に変換されます。

C++0X では、右辺値参照の導入により、右辺値参照をパラメーターとして取り、operator<< のオーバーロードを提供できるようになり、問題の根本原因が解決されることに注意してください。

于 2010-02-11T16:25:04.337 に答える
3

答えの一部に気づきました。temporary は左辺値ではないため、 type の引数として使用できませんostream &

「どうすればこれを機能させることができるか」という問題が残ります...

于 2010-02-11T16:13:09.420 に答える
1

これまでのところ、どれもきれいな解決策を提供していないように思われるので、私は汚い解決策に落ち着きます:

myostream operator<<(myostream stream, char const *str) {
    std::operator<<(stream, str);
    return stream;
}

これが可能なmyostreamのは、コピーコンストラクターがあるためです。(内部的には、参照カウントによって裏付けられていstd::stringbufます。)

于 2010-02-12T08:57:45.557 に答える
0

C++11 は右辺値参照があるためこの問題を解決しますが、これは 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;
        }
};
于 2015-09-16T14:18:59.990 に答える
-1

これを引き起こす C++ の仕様はわかりませんが、なぜそうなるのかは簡単に推測できます。

一時はスタック上に存在し、通常は別の関数に渡されるか、単一の操作が呼び出されます。したがって、その上で free オペレーターを呼び出すと、次のようになります。

operator<<(myostream(cout))

これはこの操作の最後に破棄され、endl を追加する 2 番目の「<<」演算子は無効なオブジェクトを参照します。フリー "<<" 演算子からの戻り値は、破棄された一時オブジェクトへの参照になります。C++ 仕様では、このシナリオが C++ プログラマーをイライラさせたり混乱させたりしないように、自由演算子に関する規則を定義している可能性があります。

現在、一時的な "<<(void*)" メンバー演算子の場合、戻り値はオブジェクト自体であり、まだスタック上にあり、破棄されていないため、コンパイラーはそれを破棄せずに渡すことを認識しています。それを次のメンバー演算子、つまり 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
于 2010-02-11T16:20:00.447 に答える