chr*_*ris 58 c++ standards language-lawyer
我与某人谈论了垮台事件std::stoi.坦率地说,它在std::strtol内部使用,如果报告错误则抛出.但是,根据他们的说法,std::strtol不应该为输入报告错误"abcxyz",导致stoi不抛出std::invalid_argument.
首先,这里有两个在GCC上测试过的关于这些案例行为的程序:
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.
Run Code Online (Sandbox Code Playgroud)
总结了依赖的行为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.
Run Code Online (Sandbox Code Playgroud)
鉴于传入的情况"abc",C标准规定nptr,指向字符串的开头,将存储endptr,指针传入.这似乎与测试一致.此外,应返回0,如下所述:
§7.22.1.4
If no conversion could be performed, zero is returned.
Run Code Online (Sandbox Code Playgroud)
之前的参考文献表示不会执行任何转换,因此它必须返回0.这些条件现在符合C++ 11 stoi投掷标准std::invalid_argument.
这对我很重要,因为我不想推荐stoi作为其他字符串转换为int的更好的替代方法,或者自己使用它就好像它按照你期望的方式工作,如果它没有将文本捕获为无效转换.
所以这一切之后,我在某个地方出错了吗?在我看来,我有很好的证据证明这个例外被抛出.我的证明是否有效,或者std::stoi不能保证在给出时抛出该异常"abc"?
Gen*_*man 73
std::stoi在输入上抛出错误"abcxyz"?是.
我认为你的困惑可能来自于除了溢出之外strtol从不报告错误的事实.它可以报告没有执行转换,但这在C标准中从未被称为错误条件.
strtol所有三个C标准都是类似地定义的,我将为您提供无聊的细节,但它基本上定义了一个"主题序列",它是与实际数字对应的输入字符串的子串.以下四个条件是等效的:
*endptr != nptr(这只在endptr非空时才有意义)当存在溢出时,仍然认为转换已经发生.
现在,很明显因为"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;
}
Run Code Online (Sandbox Code Playgroud)
这意味着当给定输入而没有可选的基本参数时,std::stoi 必须抛出任何一致的实现.invalid_argument"abcxyz"
std::stoi有令人满意的错误检查?当号她说,你和她说话的人是正确的std::stoi比进行全面检查较为宽松errno == 0 && end != start && *end=='\0'后std::strtol,因为std::stoi默默除掉从字符串中的第一个非数字字符开始的所有字符.
事实上,在我的脑海中,唯一的语言,其原生转换行为有点像std::stoiJavascript,即使这样,你必须强制基数10 parseInt(n, 10)以避免十六进制数字的特殊情况:
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...
Run Code Online (Sandbox Code Playgroud)
注意:在处理空格和冗余+符号时,语言之间也存在差异.
我不知道有任何内置功能可以做到这一点,但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;
}
}
Run Code Online (Sandbox Code Playgroud)