【发布时间】:2019-06-14 01:12:09
【问题描述】:
我正在使用可爱的 nlohmann::json 处理一些 JSON 解析代码,并且为了帮助生成有用的错误消息,我自己创建了一个函数来打印 JSON 对象的类型。这个函数接受一个json::value_t,它是一个枚举类,在json.hpp中定义如下:
enum class value_t : std::uint8_t {
null,
object,
array,
string,
boolean,
number_integer,
number_unsigned,
number_float,
discarded
};
这是我的功能。我给它传递了一个json::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 中调试时,当我非常肯定地传递了 json::value_t::number_float 时,这个函数返回了字符串 "an integer",我真的被吓到了。
担心最坏的情况,并想要快速修复,我编写了以下替代方案,除了枚举在使用之前始终转换为其基础类型之外,它是相同的:
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"。
仍然好奇,并怀疑微软的怪癖或未定义行为之一潜伏在我相当大的代码库的其他地方,I ran the following test on 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。但是根据this answer 的说法,比较普通的enum 并没有什么特别之处。使用enum class 有什么不同吗?这是标准行为吗?
编辑:这是一个更简单的混淆来源:
看来,如果我使用< 比较任何一对最后三个枚举类值,它总是返回false。这可能是我上面问题的核心。为什么会有这种奇怪的行为?以下输出来自this live example
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
【问题讨论】:
-
您可能错过了
value_t的免费operator<。 -
发布Minimal, Reproducible Example而不是一堆sn-ps会改善问题
-
@RetiredNinja 你是对的。我应该在源代码中进一步向下滚动。
-
@RetiredNinja 哎哟......看起来像是一个通常被认为是精心设计的库的错误(提供
operator<,它没有给出严格的弱排序) -
@M.M 实际上它给出了严格的弱排序,它只是认为这 3 个是相等的。
标签: c++ string enums nlohmann-json