首先,我认为对于无符号值解析负值是错误的。该值由
std::num_get<char>
根据
strtoull()
格式进行解码(22.4.2.12段3,第3阶段,第二个项目)。
strtoull()
的格式在C 7.22.1.4中定义为与C 6.4.4.1中的整数常量相同,其要求字面值可以由
unsigned
类型表示。显然,负值不能由
unsigned
类型表示。不可否认,我查看了C11,但它并非C++11引用的C标准。此外,在编译器引用标准段落也无法解决问题。因此,下面是一种简洁地更改值解码的方法。
您可以使用拒绝以负号开头的字符串的
std::num_get<...>
facet为
unsigned long
和
unsigned long long
设置全局
std::locale
。
do_put()
覆盖可以简单地检查第一个字符,如果不是
'-'
则委托给基类版本。
以下是自定义facet的代码。虽然这是相当多的代码,但实际使用非常简单。大部分代码只是模板覆盖不同
virtual
函数,用于解析
unsigned
数字(即,
do_get()
成员)。这些都是基于成员函数模板
get_impl()
实现的,该模板检查是否没有更多字符或下一个字符是否为
'-'
。在这两种情况下,通过将
std::ios_base::failbit
添加到参数
err
中来使转换失败。否则,该函数仅委托给基类转换。
最终创建的facet最终用于构建新的
std::locale
对象(
custom
;请注意,分配的
positive_num_get
对象在使用它的最后一个
std::locale
对象被释放时会自动释放)。此
std::locale
被安装为全局语言环境。全局语言环境由所有新创建的流使用。对于现有流,在示例中为
std::cin
,如果应该影响它们,则需要
imbue()
使用语言环境。一旦设置了全局区域设置,新创建的流将只需选择已更改的解码规则,因此不应太需要更改代码。
#include <iostream>
#include <sstream>
#include <locale>
class positive_num_get
: public std::num_get<char> {
typedef std::num_get<char>::iter_type iter_type;
typedef std::num_get<char>::char_type char_type;
template <typename T>
iter_type get_impl(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
T& val) const {
if (in == end || *in == '-') {
err |= std::ios_base::failbit;
return in;
}
else {
return this->std::num_get<char>::do_get(in, end, str, err, val);
}
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned short& val) const override {
return this->get_impl(in, end, str, err, val);
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned int& val) const override {
return this->get_impl(in, end, str, err, val);
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned long& val) const override {
return this->get_impl(in, end, str, err, val);
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned long long& val) const override {
return this->get_impl(in, end, str, err, val);
}
};
void read(std::string const& input)
{
std::istringstream in(input);
unsigned long value;
if (in >> value) {
std::cout << "read " << value << " from '" << input << '\n';
}
else {
std::cout << "failed to read value from '" << input << '\n';
}
}
int main()
{
read("\t 17");
read("\t -18");
std::locale custom(std::locale(), new positive_num_get);
std::locale::global(custom);
std::cin.imbue(custom);
read("\t 19");
read("\t -20");
}