You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20中memmove返回值经static_cast转换为A*后操作的未定义行为判定咨询

C++20中memmove返回值经static_cast转换为A*后操作的未定义行为判定咨询

嘿,这个问题问到了C++20对象模型里很容易踩坑的点——隐式对象创建(IOC)和指针的“标准授权”问题,咱们一步步来理清楚:

首先得明确C++20里memmove的特殊待遇:根据标准描述,memmove的调用会隐式在目标存储区域创建对象,而且它返回的void*是被标准“认可”的——也就是这个指针直接指向memmove刚创建的那个对象的存储起始位置。

第一个场景:直接调用memmove后转换指针

先看这段代码:

struct A { int n; };
auto a = A{1};
void* p1 = &a;
alignas(A) char buf[8];
auto p2 = static_cast<A*>(memmove(buf, p1, sizeof(A)));
p2->n = 2; // Is this technically UB since C++20?

这里完全没问题,不是未定义行为。因为memmove的调用已经在buf里隐式创建了一个A类型的对象,它返回的void*直接指向这个对象的存储位置。把这个指针static_cast成A*是合法的,p2确实指向一个有效的A对象,所以修改p2->n是完全符合标准的操作。

第二个场景:通过lambda包装memmove后转换指针

再看这个例子:

auto fn = [&] { return memmove(buf, p1, sizeof(A)); };
// Must it be `auto p3 = std::start_lifetime_as<A>(fn());` here?
auto p3 = static_cast<A*>(fn()); // fn is not blessed by the standard as memmove.
p3->n = 3; // Is this technically UB since C++20?

这里的情况就不一样了,直接转换后操作确实是未定义行为。

原因在于:标准只给memmove这类明确列出的函数的返回指针“开绿灯”——认可它们指向刚创建的对象。但当你把memmove的调用包装在一个普通lambda(或者任何非标准特殊标记的函数)里时,lambda本身不是标准“授权”的函数,它返回的void*就失去了和“刚创建的A对象”的关联标记。编译器/标准无法追踪到普通函数内部的操作,所以从fn()得到的void*,你不能直接通过static_cast<A*>来获取指向A对象的有效指针——这里存在一个指针和对象的关联断裂。

而你提到的std::start_lifetime_as<A>(fn())才是正确的做法:这个函数的作用就是显式告诉编译器,“这个void*指向的存储区里存在一个A类型的对象,我要获取它的合法指针”。用这个方式得到的p3就是合法的指向A对象的指针,操作p3->n就完全没问题了。

总结一下核心区别:C++20的隐式对象创建规则里,只有标准明确标注的操作(比如memmove)的返回指针,才能直接转换为对应对象类型的指针;如果通过普通函数/ lambda包装这些操作,返回的指针需要用std::start_lifetime_as来显式建立和对象的关联,否则直接转换后操作就是未定义行为。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 09:23:07