4

コンパイラを gcc-4.4 から gcc-4.8 にアップグレードしましたが、次の (誤った) 仮定に起因して、1 つのプロジェクトが惨めに失敗しました。

#include <sstream>
#include <assert.h>

int main()
{
    using namespace std;
    istringstream iScan;
    int num;

    //iScan.unsetf(std::ios::skipws);
    iScan.str("5678");
    iScan >> num;
    assert(iScan.tellg() == istringstream::pos_type(4));
    assert(!iScan.fail());
    assert(!iScan.good());
    assert(iScan.eof());
    assert(num == 5678);
    assert(false && "We passed the above assertions.");
    return 0;
}

gcc-4.4 では、関連するアサーションがパスします。gcc-4.8 では、tellg() は -1 を返し、fail() は !false を返します。これは明らかに eof にヒットしたためです。

私のターゲットは、Qt 5.1 (gcc-4.8) に同梱されている MinGW 32 ビットです。

質問:

  • N3168またはその他によると、古い動作は本当に間違っていますか? (その他の?)
  • グローバルで信頼性が高く、言語に依存しない回避策はありますか? (そうではないと思います。)
  • バージョンにまたがるグローバルで信頼性の高い gcc の回避策はありますか?
  • 上記の unsetf(skipws) を実行しても、gcc-4.8 では動作しません。それは不正行為ではないでしょうか?

さらに、さまざまなオンライン コンパイラが異なる動作をします。それは彼らのlibの機能ですか?

その他の類似するが重複しない質問:

これらの仮定がすべて実行されているコード本体は、大規模です。

更新: これが答えの重要な部分です。これは、すべてのバージョン、すべてのコンパイラで機能するはずです:

// istream::tellg() is unreliable at eof(): works w/gcc-4.4, doesn't w/gcc-4.8.
#include <sstream>
#include <assert.h>

using namespace std;
typedef istream::pos_type   pos_type;

pos_type reliable_tellg(istream &iScan)
    {
    bool wasEOF = iScan.eof();
    if (wasEOF)
        iScan.clear(iScan.rdstate() & ~ios::eofbit); // so tellg() works.
    pos_type r = iScan.tellg();
    if (wasEOF)
        iScan.clear(iScan.rdstate() | ios::eofbit); // restore it.
    return r;
    }


int main()
{
    istringstream iScan;
    int num, n2;

    //iScan.unsetf(std::ios::skipws);
    iScan.str("5678");
    assert(!iScan.eof() && !iScan.fail()); // pre-conditions.
    assert(reliable_tellg(iScan) == pos_type(0));

    iScan >> num;
    assert(!iScan.fail());
    assert(reliable_tellg(iScan) == pos_type(4));
    assert(iScan.eof());
    assert(reliable_tellg(iScan) == pos_type(4)); // previous calls don't bungle it.
    assert(num == 5678);

    iScan >> n2; // at eof(), so this should fail.
    assert(iScan.fail());
    assert(reliable_tellg(iScan) == pos_type(-1)); // as expected on fail()
    assert(iScan.eof());

    assert(false && "We passed the above assertions.");
    return 0;
}
4

1 に答える 1

2

あなたが期待しているように見える動作は、おそらく間違っています。C++11 と C++03 の両方でtellg、「フォーマットされていない入力関数として動作する [...]」で記述を開始します。「フォーマットされていない入力関数」は、オブジェクトを構築することから始まり、オブジェクトが に変換されるsentryと失敗し、何もせずに失敗ステータスを返し ます。が設定されている場合、オブジェクトは に変換されます。sentryfalsesentryfalseeofbit

数値を読み取ることで が設定されるかどうかについて、標準ではやや明確ではありませんeofbitが、ごくわずかです (情報はいくつかの異なるセクションに分散しています)。基本的に、数値を入力するとき、ストリーム (実際にはnum_getファセット) は、数値がどこで終わるかを知るために、1 文字先を読み取る必要があります。あなたの場合、これが発生するとファイルの終わりが表示されるため、 が設定されeofbitます。したがって、最初assertの実装は準拠した実装で失敗します。

これは、標準の欠陥、または意図的ではないと簡単に考えることができます。おそらく、元の実装者が標準の完全な意味を理解していなかった (または無意識のうちに、標準を読むべきだと思ったように読んだ) ため、いくつかの実装が賢明なことを行っていることを想像するのは非常に簡単です (これはあなたが期待しているように思われることです)。これはg ++の場合であると推測し、彼らの動作が準拠していないことに気付いたとき、彼らはそれを修正しました.

回避策については...本当の問題が何であるかはわかりません。回避しようとしているということです。しかし、の前にエラービットをクリアすれば、うまくtellgいくはずです。(もちろん、と になりiScan.good()ます。しかし、これは本当に問題になるのでしょうか?) ステータスをクリアする前に、抽出が実際に成功したことを確認してください。trueiScan.eof() false

于 2013-08-28T14:46:02.943 に答える