10

コード:

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
using std::cerr;
using std::cout;
using std::stringstream;
using std::string;
using std::for_each;

void convert(const string& a_value)
{
    unsigned short i;
    if (stringstream(a_value) >> i)
        cout << a_value << " converted to " << i << ".\n";
    else
        cerr << a_value << " failed to convert.\n";
}

int main()
{
    string inputs[] = { "abc", "10", "999999999999999999999", "-10", "0" };
    for_each(inputs, inputs + (sizeof(inputs)/sizeof(inputs[0])), convert);
    return 0;
}

Visual Studioコンパイラ(v7、v8、v9、v10)からの出力:

abcは変換に失敗しました。
10を10に変換します。
999999999999999999999は変換に失敗しました。
-10は65526に変換されます。
0は0に変換されます。

g ++(v4.1.2、v4.3.4)からの出力:

abcは変換に失敗しました。
10を10に変換します。
999999999999999999999は変換に失敗しました。
-10は変換に失敗しました。
0は0に変換されます。

"-10"に変換できないと思っていましたがunsigned short、VCコンパイラでは成功します。これは:

  • VCコンパイラのバグ?
  • GNUコンパイラのバグと私は間違った期待を持っていますか?
  • 実装で定義された動作?
4

2 に答える 2

5

答えは、使用しているC++のバージョンによって異なります。C ++ 03以前では、入力sscanfが(ここでは"%hi"入力指定子を使用して)行うものに準拠する必要がありsscanf、オーバーフロー検出なしで、整数値を(符号付き)shortに読み込みます。次に、結果が(暗黙の変換を使用して)に割り当てられますunsigned short。C ++ 11にはstrtoull、符号を許可しない を呼び出すのと同等の機能が-必要であり、オーバーフローの場合はエラーが必要です(これは、の未定義動作でsscanfあるため、C ++ 03)。

実際には、C ++ 03のすべての合理的な実装は低すぎることをチェックし、そのような場合の「未定義の動作」は現在必要とされているものに対応していました。一方、現在(論理的に)禁止されているマイナス記号を受け入れる必要がありました。

編集(修正):の要件を読み直すとstrtoull、マイナス記号を受け入れる必要があることがわかりました。そのため、見た目はばかげていますが、標準では、マイナス記号を受け入れるために符号なし整数型への入力が必要です。(の動作はstrtoullグローバルCロケールに依存することにも注意してください。これにより、追加の可能性が受け入れられる場合があります。)

編集(さらに明確にする):ectamurが指摘しているように、これは(C ++ 11では)エラーになるはずです。これは、(unsigned long long)( -10 )で表すには大きすぎるためunsigned shortです。一方、C ++ 03より前のバージョンではまだ未定義の動作です(これはおそらくVC ++が準拠しているものです---したがって、それらが行うことはすべて「正しい」です)。

于 2012-10-24T09:13:27.493 に答える
3

g++は正しいです。num_get<>符号なし整数型の算術エクストラクタは、27.7.2.2.2p1で;に依存するものとして定義されています。22.4.2.1.2p3は次のように述べています。

ステージ3:ステージ2(フィールド)に蓄積されたsのシーケンスはchar、[...]の規則によって数値に変換されます—符号なし整数値の場合、関数strtoull

保存される数は

—フィー​​ルドが正すぎてで表現できない値を表す場合、最も正の表現可能な値valios_base::failbitに割り当てられerrます。

の操作に関してstrtoull、C ++はCに延期されます。これは、負の符号のフィールドをstrtoull;で変換しようとした結果については少し不明確です。これは、「変換の結果の値が(戻り型で)否定される」と述べています。これunsigned long longにより、(to)の符号が折り返されULONGLONG_MAX - 10 + 1ます。

したがって、で表現するにstrtoullは大きすぎる値を返し、フェイルビットを格納および設定するために必要です。unsigned shortnum_getUSHORT_MAX

一方、22.4.2.1.2p3は、格納される数は(私の強調)である必要があるとも述べています。

—フィー​​ルドがで表すには大きすぎる負の値を表す場合は、最も負の表現可能な値、または符号なし整数型の場合はゼロvalios_base::failbitに割り当てられerrます。

この句の存在は、strtoull負の符号を持つフィールドについては、の規則に厳密に従わないことを示します。この解釈では、フェイルビットnum_getを保存および設定する必要があります。0

いずれの場合も、変換は失敗する必要があります。

于 2012-10-24T09:12:54.537 に答える