std::stoi
の没落について誰かと素敵な会話をしました。率直に言って、std::strtol
を内部的に使用し、エラーが報告された場合はスローします。しかし、彼らによれば、std::strtol
は"abcxyz"
の入力に対してエラーを報告してはならず、stoi
はstd::invalid_argument
をスローしません。
まず、これらのケースの動作についてGCCでテストされた2つのプログラムを次に示します。
strtol
stoi
両方とも"123"
で成功を示し、"abc"
で失敗を示します。
私はより多くの情報を引き出すために標準を見ました:
§21.5
Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that
no conversion could be performed. Throws out_of_range if the converted value is
outside the range of representable values for the return type.
これは、strtol
に依存する動作を要約しています。 strtol
はどうですか?私はこれをC11ドラフトで見つけました:
§7.22.1.4
If the subject sequence is empty or does not have the expected form, no
conversion is performed; the value of nptr is stored in the object
pointed to by endptr, provided that endptr is not a null pointer.
"abc"
を渡す状況を考えると、C標準は、文字列の先頭を指すnptr
が、渡されるポインターであるendptr
に格納されることを規定しています。テストと一致しているようです。また、次のように0が返されます。
§7.22.1.4
If no conversion could be performed, zero is returned.
前のリファレンスでは、変換は実行されないため、0を返す必要があると述べていました。これらの条件は、stoi
throwing std::invalid_argument
のC++ 11標準に準拠しています。
この結果は私にとって重要です。なぜなら、他の文字列からintへの変換方法の代わりとしてstoi
を推奨したり、期待どおりに機能するかのように自分で使用したりしたくないからです。 、テキストが無効な変換としてキャッチされない場合。
それで、このすべての後、私はどこか間違ったことをしましたか?私には、この例外がスローされることの良い証拠があるように思えます。私の証明は有効ですか、またはstd::stoi
は、"abc"
が与えられたときにその例外をスローすることが保証されていませんか?
std::stoi
_は、入力_"abcxyz"
_でエラーをスローしますか?はい。
あなたの混乱は、strtol
がオーバーフローを除いてエラーを決して報告しないという事実から来ると思います。変換が実行されなかったことを報告できますが、これはC標準ではエラー状態と呼ばれることはありません。
strtol
は3つのC標準すべてで同様に定義されており、退屈な詳細は省きますが、基本的には、実際の番号に対応する入力文字列のサブストリングである「サブジェクトシーケンス」を定義します。次の4つの条件は同等です。
*endptr != nptr
_(これはendptr
がnull以外の場合にのみ意味があります)オーバーフローがある場合、変換はまだ発生したと言われます。
ここで、_"abcxyz"
_には数値が含まれていないため、文字列_"abcxyz"
_のサブジェクトシーケンスは空にして、変換を実行できないようにする必要があることは明らかです。次のC90/C99/C11プログラムは、実験的に確認します。
_#include <stdio.h>
#include <stdlib.h>
int main() {
char *nptr = "abcxyz", *endptr[1];
strtol(nptr, endptr, 0);
if (*endptr == nptr)
printf("No conversion could be performed.\n");
return 0;
}
_
これは、オプションの基本引数なしで入力_std::stoi
_が指定された場合、_invalid_argument
_ must throw _"abcxyz"
_の準拠実装を意味します。
std::stoi
_に十分なエラーチェックがあることを意味しますか?いいえ。_std::stoi
_はすべての文字を静かに削除するため、_errno == 0 && end != start && *end=='\0'
_は_std::strtol
_の後に完全なチェック_std::stoi
_を実行するよりも寛大であると言ったときにあなたが話していた人は正しいです。文字列の最初の非数値文字から始まります。
実際、ネイティブ変換が_std::stoi
_のように動作する唯一の言語はJavascriptであり、その場合でもparseInt(n, 10)
を使用してベース10を強制し、16進数の特殊なケースを回避する必要があります:
_input | std::atoi std::stoi Javascript full check
===========+=============================================================
hello | 0 error error(NaN) error
0xygen | 0 0 error(NaN) error
0x42 | 0 0 66 error
42x0 | 42 42 42 error
42 | 42 42 42 42
-----------+-------------------------------------------------------------
languages | Perl, Ruby, Javascript Javascript C#, Java,
| PHP, C... (base 10) Python...
_
注:空白と冗長な+記号の処理には言語間でも違いがあります。
私はこれを行う組み込み関数を知りませんが、_boost::lexical_cast<int>
_はあなたが望むことをします。 Pythonのint()
関数とは異なり、周囲の空白も拒否するため、特に厳密です。無効な文字とオーバーフローは、同じ例外_boost::bad_lexical_cast
_になることに注意してください。
_#include <boost/lexical_cast.hpp>
int main() {
std::string s = "42";
try {
int n = boost::lexical_cast<int>(s);
std::cout << "n = " << n << std::endl;
} catch (boost::bad_lexical_cast) {
std::cout << "conversion failed" << std::endl;
}
}
_