You need to enable JavaScript to run this app. 在同一语句中使用并移动unique_ptr是否合法?为何部分场景崩溃?
优惠活动
大模型
产品
解决方案
定价
更多

在同一语句中使用并移动unique_ptr是否合法?为何部分场景崩溃?

问题解答

1. 同语句使用并移动unique_ptr的合法性与求值顺序

  • 这种写法的合法性完全依赖C版本和参数求值顺序,**C17之前属于未定义行为**,即使是C++17及以上版本也存在风险。
  • C++的求值顺序规则:
    • 对于成员函数调用,所有C++版本都保证成员函数的对象表达式(也就是self->socket、self->t这部分)的求值,一定早于所有函数参数的求值。
    • 但同一个函数的多个参数之间的求值顺序,在所有C++版本中都是未指定的,编译器可以按任意顺序求值多个参数。
  • 你崩溃的代码中,async_send_to有3个参数:
    1. self->frame->get_asio_buffer()
    2. self->client_endpoint
    3. std::bind(...)内部执行std::move(self)转移所有权
      如果编译器先求值第三个参数,把self的所有权转移走,self变为空,再去求值前两个参数时解引用空的unique_ptr,就会触发段错误,完全符合你遇到的崩溃现象。

2. 栈追踪的判断正确性

你的判断是正确的。错误提示访问零页偏移0x90的地址,就是对空的unique_ptr执行operator->访问成员时,成员偏移量为0x90,触发了非法内存访问。shared_ptr_base.h的报错只是解引用空指针后,最终访问frame成员(shared_ptr类型)的get方法时触发的崩溃,根因还是self已经为空。

3. 样例代码正常运行的原因

你的样例中async_wait只有1个参数,参数求值顺序不存在歧义:

  1. 首先求值成员函数的对象表达式self->t,这一步会先解引用self获取timer对象,此时还没有执行move操作,完全安全。
  2. 再求值唯一的参数,也就是std::bind内部的std::move(self),此时所有对self的访问已经完成,move不会影响之前的操作。
    所以样例代码不管什么编译器版本,执行顺序都是固定安全的,不会触发崩溃。

修复方案

最简单的修复方式是先把需要用到的成员变量提前取出来,再执行move操作,完全规避求值顺序问题:

auto buffer = self->frame->get_asio_buffer();
auto endpoint = self->client_endpoint;
self->socket.async_send_to(buffer, endpoint,
                           std::bind(&download_server::receiver, std::move(self), std::placeholders::_1, 
                           std::placeholders::_2));

如果仅面向C17及以上标准编译,也可以直接依赖C17的求值规则,但提前取值的写法兼容性更好,也更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 23:54:02