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

MSVC使用memcpy复制lambda时出现异常行为的原因咨询

问题:使用memcpy复制std::function时MSVC下的异常行为

我需要将std::function存入公司内部实现的vector容器,该容器内部使用memcpy而非拷贝/移动运算符,这要求存入的元素必须是平凡可复制类型。我编写了测试程序复现问题:GCC编译下输出符合预期,但MSVC下运行异常。

测试代码

#include<iostream>
#include<functional>
#include<cstring>
using namespace std;

using VAR = void();
struct Abc
{
    std::function<VAR> fn;
};

struct Test{
    int i;
};

void openTest(const Test& a) noexcept
{
    cout<<"In Test"<<a.i<<endl;
};

void foo(Abc& abc, const Test& test)
{
    abc.fn = [test]() noexcept{ openTest(test);};
}
int main()
{
    Abc arr[2];

    for(int i=0;i<2;++i)
    {
        Abc abc;
        memset(&abc,0, sizeof(Abc));

        Test test{i*8+1};
        foo(abc,test);
        memcpy(&arr[i],&abc,sizeof(Abc));
        // 填充数组时查看结果
        arr[i].fn();
    }
    // 填充完成后查看结果
    for(int i=0;i<2;++i)
        arr[i].fn();
}

不同编译器执行结果

GCC(x86-64 gcc 12.2)

In Test1
In Test9
In Test1
In Test9

MSVC(x86 msvc v19.latest)

In Test1
In Test9
In Test9
In Test9

调试发现,第二次循环(i==1)执行memcpy时,会同时更新arr[0].fn和arr[1].fn,请问为何MSVC会出现这种异常行为?


解答

核心原因:未定义行为导致的差异

首先必须明确:std::function不是C++标准定义的平凡可复制类型。根据C++标准,只有平凡可复制类型才能通过memcpy安全复制,否则行为完全未定义——GCC下的“正常输出”只是巧合,并非合法的可预期行为。

MSVC异常表现的具体原因

MSVC的std::function实现采用了小对象优化(SSO):对于体积较小的捕获对象(比如示例中的Test结构体),会直接将捕获数据存储在std::function内部的缓冲区中,而非堆上。但当你用memcpy复制std::function时,跳过了它的拷贝构造函数,破坏了内部的状态管理逻辑(比如引用计数、存储指针的正确性)。

在你的测试代码中,第二次循环的栈对象abc的std::function内部存储可能和arr[0].fn的内部存储存在地址重叠,或者因为未定义行为导致指针指向错误,最终memcpy操作意外覆盖了arr[0].fn的内部数据,使得两个std::function都指向了第二次循环的捕获对象。

可行的解决方案

如果公司的vector必须依赖memcpy存储元素,那么不能直接存储std::function,可以考虑以下替代方案:

  • 使用普通函数指针:但仅适用于无状态的函数,无法捕获外部变量;
  • 自定义平凡可复制封装类型:手动封装函数指针和平凡可复制的捕获数据,自行管理内存;
  • 协调修改公司vector的实现:使其支持非平凡可复制类型的拷贝/移动操作,这是最合规的方案。

内容的提问来源于stack exchange,提问作者abhishek gupta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 17:55:30