如何解决cppreference 上 std::uninitialized_default_construct 示例中的未定义行为?
在他们的 std::uninitialized_default_construct
的 example 中:
struct S { std::string m{ "Default value" }; };
constexpr int n {3};
alignas(alignof(S)) unsigned char mem[n * sizeof(S)];
try
{
auto first {reinterpret_cast<S*>(mem)};
auto last {first + n}; // (1)**********
std::uninitialized_default_construct(first,last);
// (2)**********
for (auto it {first}; it != last; ++it) {
std::cout << it->m << '\n';
}
std::destroy(first,last);
}
catch(...)
{
std::cout << "Exception!\n";
}
// Notice that for "trivial types" the uninitialized_default_construct
// generally does not zero-fill the given uninitialized memory area.
int v[] { 1,2,3,4 };
const int original[] { 1,4 };
std::uninitialized_default_construct(std::begin(v),std::end(v));
// (3)**********
// for (const int i : v) { std::cout << i << ' '; }
// Maybe undefined behavior,pending CWG 1997.
- 既然
S
和unsigned char
不是指针可互转换,那么last
会指向mem
的结尾吗?此处是否存在指针算术未定义行为? - 是否需要 std::launder 来使
first
和last
实际上指向S
?例如first = std::launder(first); last = first + n;
- 打印
v
的元素真的是未定义的行为吗? CWG 1997 谈论unsigned char[]
存储,但上面的示例已使用v
初始化int
。
解决方法
-
unsigned char
是1 byte
的另一种说法,因此分配是按照struct S
的大小(以字节为单位)乘以元素数n
.mem
的类型为unsigned char []
,因此,根据 [conv.array]/1,它将相当于指向数组中第一个元素的unsigned char *
,并且根据 {{3} }}reinterpret_cast
会将其转换为struct S*
,这将导致声明为first
的auto
被创建为struct S*
,因此语句可与C
形式互换:
由于struct S* first = (struct S*) &mem
first
被解析为struct S*
,因此last
也将被解析为相同的类型并且指针算法将正常工作。 - 不需要
std::launder
,因为语句auto first {reinterpret_cast<S*>(mem)};
已经初始化了指向mem
中第一个元素的指针,并且带有指针算法的下一条语句已经设置了 { {1}} 到第三个元素。 - 我认为从 [expr.reinterpret.cast]/4 开始这是未定义的行为是正确的。
last
是int []
的类型,没有默认构造函数,因此无法初始化v
。根据定义,这些值将保持未确定状态。令人困惑的部分是v
的元素已经被编译器初始化为v
,因此期望这些值将被保留(因为它们已经在内存位置中),但是从标准从角度来看,无法确定1,2,3,4
是否会在v
之后保持此值。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。