gpt4 book ai didi

c++ - 比较枚举类值时的奇怪行为

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:26:17 25 4
gpt4 key购买 nike

我正在使用可爱的 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 中调试时,当这个函数返回字符串 "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" ,如我所料。

仍然好奇,并怀疑微软的一个怪癖或未定义行为潜伏在我相当大的代码库的其他地方,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_floatnumber_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

最佳答案

你有这个问题是因为有 operator<为此枚举提供:

inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
static constexpr std::array<std::uint8_t, 8> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
}
};

const auto l_index = static_cast<std::size_t>(lhs);
const auto r_index = static_cast<std::size_t>(rhs);
return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
}

来自 here

并且根据这段代码integer , unsignedfloat被认为是平等的,那么你的问题。

作为解决方案,您可以使用您的方法或简单地用 lambda 替换默认比较器或为 std::less 提供特化没有使用这个运算符。

关于c++ - 比较枚举类值时的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56590243/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com