std::as_writable_bytes何时触发UB?g函数是否无未定义行为?
关于reinterpret_cast、严格别名规则与std::span/std::as_writable_bytes的问题
代码示例
#include <span> int g() { bool b[1] = {true}; std::span<bool> bs(b); auto ps = std::as_writable_bytes(bs); ps[0] = std::byte{10}; return b[0] == 0; } int h() { bool b{true}; std::span<bool> bs(&b, 1); auto ps = std::as_writable_bytes(bs); ps[0] = std::byte{10}; return b == 0; }
问题解答
1. g函数是否无未定义行为?原因是什么?
g函数没有未定义行为,原因如下:
b是bool数组,数组元素的生命周期从初始化到函数结束持续有效。std::span<bool>绑定数组是标准允许的合法操作,此时span代表数组中已存在的bool对象的连续视图。- 根据严格别名规则,用
std::byte访问任何对象的字节表示是允许的。std::as_writable_bytes获取的是数组元素的字节级可写视图,修改ps[0]本质是直接修改数组中已存在的bool对象的字节内容,并未终止该对象的生命周期,完全符合标准要求。
2. 若g与h存在相同问题,std::as_writable_bytes的用途及正确用法是什么?
首先明确:g函数没有问题,仅h函数存在未定义行为。
- 用途:
std::as_writable_bytes用于获取连续存储对象(如数组、std::array、容器的连续存储段等)的可写字节视图,主要用于序列化、内存拷贝、字节级校验等底层字节操作场景。 - 正确用法:
- 仅用于操作已存在对象的字节表示,避免通过字节覆盖的方式“替换”对象(即防止因覆盖存储导致原对象生命周期意外终止)。
- 优先绑定数组、
std::span<T>(T为非特化类型,或特化但对应连续存储的场景如std::span<bool>绑定数组)这类明确包含持续生命周期对象的连续存储结构。 - 若要修改单个标量对象的字节,建议用
std::memcpy将字节数据复制到对象中,而非直接通过字节视图覆盖存储。
3. 为非数组元素创建std::span是否非法?
不非法。标准明确允许std::span绑定单个对象,通过std::span<T>(&obj, 1)的方式创建视图是合法的。h函数的问题并非出在创建span本身,而是后续通过字节视图覆盖单个标量对象的存储,导致原对象生命周期终止的操作。
4. 这些问题是否仅针对bool类型?
不是。该问题本质是单个标量对象的存储被字节覆盖后,原对象生命周期终止且未正确构造新对象导致的未定义行为,适用于所有标量类型(如int、char、double等)。例如将h函数中的bool替换为int,执行相同的字节覆盖操作,依然会触发未定义行为;而数组元素的字节修改操作,无论标量类型是什么,只要元素生命周期有效,就不会有问题。
内容的提问来源于stack exchange,提问作者Erik Man
相关产品推荐
相关产品推荐

