我正在使用可爱的 nlohmann::json 处理一些 JSON 解析代码,为了帮助生成有用的错误消息,我为自己创建了一个函数来打印 JSON 对象的类型。此函数接受 a json::value_t
,它是一个枚举类,其定义如下json.hpp
:
enum class value_t : std::uint8_t {
null,
object,
array,
string,
boolean,
number_integer,
number_unsigned,
number_float,
discarded
};
这是我的功能。我通过它 ajson::value_t
并且我希望收到一个描述它的字符串。
std::string to_string(json::value_t type){
static const std::map<json::value_t, std::string> mapping = {
{json::value_t::null, "null"},
{json::value_t::object, "an object"},
{json::value_t::array, "an array"},
{json::value_t::string, "a string"},
{json::value_t::boolean, "a boolean"},
{json::value_t::number_integer, "an integer"},
{json::value_t::number_unsigned, "an unsigned integer"},
{json::value_t::number_float, "a floating point number"}
};
auto it = mapping.find(type);
if (it != mapping.end()){
return it->second;
}
return "a mystery value";
}
但是,在 Visual Studio 中进行调试时,"an integer"
当我非常肯定地通过它时,当这个函数返回字符串时,我真的被吓到了json::value_t::number_float
。
由于担心最坏的情况,并且想要快速修复,我编写了以下替代方法,除了枚举在使用之前总是被强制转换为它的基础类型之外,它是相同的:
std::string to_string_with_cast(json::value_t type){
using ut = std::underlying_type_t<json::value_t>;
static const std::map<ut, std::string> mapping = {
{static_cast<ut>(json::value_t::null), "null"},
{static_cast<ut>(json::value_t::object), "an object"},
{static_cast<ut>(json::value_t::array), "an array"},
{static_cast<ut>(json::value_t::string), "a string"},
{static_cast<ut>(json::value_t::boolean), "a boolean"},
{static_cast<ut>(json::value_t::number_integer), "an integer"},
{static_cast<ut>(json::value_t::number_unsigned), "an unsigned integer"},
{static_cast<ut>(json::value_t::number_float), "a floating point number"}
};
auto it = mapping.find(static_cast<ut>(type));
if (it != mapping.end()){
return it->second;
}
return "a mystery value";
}
这行得通。正如我所料,通过了json::value_t::number_float
结果。"a floating point number"
仍然好奇,并怀疑微软的怪癖之一或未定义行为潜伏在我相当大的代码库的其他地方,我在 g++ 上运行了以下测试:
std::cout << "Without casting enum to underlying type:\n";
std::cout << "null: " << to_string(json::value_t::null) << '\n';
std::cout << "object: " << to_string(json::value_t::object) << '\n';
std::cout << "array: " << to_string(json::value_t::array) << '\n';
std::cout << "string: " << to_string(json::value_t::string) << '\n';
std::cout << "bool: " << to_string(json::value_t::boolean) << '\n';
std::cout << "int: " << to_string(json::value_t::number_integer) << '\n';
std::cout << "uint: " << to_string(json::value_t::number_unsigned) << '\n';
std::cout << "float: " << to_string(json::value_t::number_float) << '\n';
std::cout << "\nWith casting enum to underlying type:\n";
std::cout << "null: " << to_string_with_cast(json::value_t::null) << '\n';
std::cout << "object: " << to_string_with_cast(json::value_t::object) << '\n';
std::cout << "array: " << to_string_with_cast(json::value_t::array) << '\n';
std::cout << "string: " << to_string_with_cast(json::value_t::string) << '\n';
std::cout << "bool: " << to_string_with_cast(json::value_t::boolean) << '\n';
std::cout << "int: " << to_string_with_cast(json::value_t::number_integer) << '\n';
std::cout << "uint: " << to_string_with_cast(json::value_t::number_unsigned) << '\n';
std::cout << "float: " << to_string_with_cast(json::value_t::number_float) << '\n';
}
我真的很害怕看到与 Visual Studio 相同的行为:
Without casting enum to underlying type: null: null object: an object array: an array string: a string bool: a boolean int: an integer uint: an integer float: an integer With casting enum to underlying type: null: null object: an object array: an array string: a string bool: a boolean int: an integer uint: an unsigned integer float: a floating point number
为什么会这样?看起来number_float
和number_unsigned
都被认为等于number_integer
。但是根据这个答案,比较正常并没有什么特别之处enum
。使用 有什么不同enum class
吗?这是标准行为吗?
编辑:这是一个更简单的混淆来源:似乎如果我<
用来比较最后三个枚举类值中的任何一对,它总是返回false
. 这可能是我上面问题的核心。为什么会有这种奇怪的行为?以下输出来自此实时示例
number_integer < number_integer : false number_integer < number_unsigned : false number_integer < number_float : false number_unsigned < number_integer : false number_unsigned < number_unsigned : false number_unsigned < number_float : false number_float < number_integer : false number_float < number_unsigned : false number_float < number_float : false null < number_integer : true null < number_unsigned : true null < number_float : true bool < number_integer : true bool < number_unsigned : true bool < number_float : true