提问人:Raffaello 提问时间:9/4/2023 最后编辑:timrauRaffaello 更新时间:10/1/2023 访问量:87
std::formatter C++ 20 与联合和结构
std::formatter c++ 20 with union and struct
问:
有什么理由不使用这样的数据类型进行编译:std::format
#include <cstdint>
typedef union MIDI_EVENT_type_u
{
uint8_t val;
struct {
uint8_t low : 4;
uint8_t high : 4;
};
} MIDI_EVENT_type_u;
用于打印该值仅适用于上述值,并且 和 不会编译,并且必须转换为无符号 int。std::format
uint8_t
val
low
high
有什么原因可以喂食吗?std::format
uint8_t
例如
MIDI_EVENT_type_u type = {0};
auto a = std::format("{}", type.val);
auto b = std::format("{}", type.high); // error
auto c = std::format("{}", type.low); // error
错误是:
no instance of overloaded function "std::format" matches the argument list
argument types are: (const char [3], uint8_t)
在这种情况下,有没有办法编写自定义来自动将强制转换为未签名?std::formatter
此外,不清楚为什么即使被检测为 ,也不编译 “” 中的值,有什么原因吗?union.struct
uint8_t
外延:
在@user17732522的回答和评论之后(谢谢),对于这个 particulat 案例,有这样的解决方案(至少对于编译,不确定它是否会完全解决类型双关语):
在可能的情况下,将该结构更改为:
typedef union MIDI_EVENT_type_u
{
uint8_t val;
// the struct also give it a name and put private would be better.
struct {
uint8_t low : 4;
uint8_t high : 4;
};
constexpr uint8_t getHigh() { return high; };
constexpr uint8_t getLow() { return low; };
} MIDI_EVENT_type_u;
使用 and 方法将解决编译错误,甚至使用全局 constexpr 最终返回 .high 或 .low 值。getHigh()
getLow()
在我看来,C++ 中缺少一些东西来处理这种情况,这是自 C '70 以来的基本用例......
ERGO:在这种情况下,C++ 也可以“自动生成”或默认执行类似 2 个额外 constexpr 的事情,在我看来,在编写代码时不需要如此冗长。
相反,如果这是一个无法修改的 C 结构:
typedef union MIDI_EVENT_type_u
{
uint8_t val;
struct {
uint8_t low : 4;
uint8_t high : 4;
};
} MIDI_EVENT_type_u;
constexpr uint8_t get(const uint8_t val) { return val; };
这样它也会编译 做“基本操作”看起来过于冗长,而以前总是在 C/C++ 中工作,我真的不明白这一点。std::format("{}", get(type.low));
这在现代 C++ 标准中是否发生了变化,并且曾经被支持?
如果是后者,则不应该在下一次 C++ 迭代中考虑支持此类基本情况并使编译器在如此简单的样板代码下生成,因为它可以通过 constexpr 获取类型并返回相同的类型来解决?
答:
std::format
通过引用(特别是作为转发引用)获取其参数。
位字段不能通过引用传递。因此,您确实需要首先转换为某种类型的 prvalue,然后可以从中具体化一个临时对象,该对象可以将引用参数绑定到该对象,例如通过写入
std::format("{}", uint8_t{type.high});
或者,如果您不想重复该类型:
std::format("{}", decltype(type.high){type.high});
在 C++23 中,您可以使用新语法:auto
std::format("{}", auto{type.high});
使用位域时,标准库的很大一部分都会遇到这个问题。标准库通常通过转发引用来获取泛型参数。
位字段的行为与普通的类成员或对象不同,对它们来说几乎是二等的。我建议避免使用位域或编写返回值的访问器成员函数,以便您可以直接使用它们。如果这是一个 C API,那么我建议先围绕它编写一个合适的 C++ API。
此外,使用匿名的语法不是标准的 C++。在标准 C++ 中,A 可以是匿名的,但 A 不能。但是,编译器可能会在 C++ 中将其作为扩展支持,因为它是 C11 中的有效语法。struct
union
struct
此外,根据标准,访问并导致未定义的行为,因为是工会的活跃成员,并且只能读取活跃成员。不能使用联合进行类型双关语。然而,编译器通常支持将其作为扩展,因为它在 C 中定义了行为,但即便如此,位字段的布局也是实现定义的,例如,位字段打包到字节中的顺序取决于实现,读取这些非活动成员的结果也是如此。type.high
type.low
val
此外,假设这不是 C API,在 C++ 中是完全多余的。就够了。typedef union MIDI_EVENT_type_u { /*...*/ } MIDI_EVENT_type_u;
union MIDI_EVENT_type_u { /*...*/ };
评论
MIDI_EVENT_type_u
private:
uint8_t getHigh() const { return high; }
std::format("{}", type.getHigh());
uint8_t getHigh(const MIDI_EVENT_type_u& x) { return x.high; }
std::format("{}", getHigh(type));
constexpr
constexpr
inline
constexpr
std::bit_cast
reinterpret_cast
评论