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

如何高效返回std::optional?关于std::make_optional的优化疑问

高效返回std::optional并使用std::make_optional的优化问题

示例代码

std::optional<Path> CreateCanonicalPath(const std::string_view& path)
{
  std::error_code errorCode;
  const auto result = std::filesystem::weakly_canonical(std::filesystem::u8path(path), errorCode);
  return !errorCode ? std::make_optional(result) : std::nullopt;
}

核心疑问

  • 将result传入std::make_optional是否存在优化空间?
  • 使用std::make_optional(std::move(result))是否更优?
  • 手动std::move是否会阻止RVO或NVRO?

解答

1. 先解决关键前提:const修饰的result无法被移动

原代码中result被const修饰,此时调用std::move(result)会得到const Path&&类型的右值,而标准库多数移动构造函数仅接受非const右值引用,这会导致std::move退化为拷贝操作——相当于没加std::move。所以第一步必须去掉const,才能让移动操作生效。

2. 手动std::move确实更优(当Path移动开销低于拷贝时)

result是函数内的局部变量,但它并非直接作为return语句的返回值,而是作为参数传递给std::make_optional。这种场景下,编译器无法触发NVRO(命名返回值优化)——因为返回的是std::make_optional创建的std::optional<Path>临时对象,而非result本身。

如果Path类型包含堆内存、文件句柄等资源,其移动构造开销远低于拷贝构造,手动添加std::move可以避免一次不必要的拷贝,直接将result的资源转移到std::optional内部存储的对象中,有效提升效率。

3. 手动std::move不会阻止RVO/NVRO

RVO/NVRO针对的是直接返回局部变量的场景,而本函数返回的是std::make_optional生成的临时std::optional<Path>对象。编译器依然可以对这个临时对象触发RVO(返回值优化),直接将其构造到函数的返回值内存位置,不会因为std::move(result)而被阻止。

优化后的代码

std::optional<Path> CreateCanonicalPath(const std::string_view& path)
{
  std::error_code errorCode;
  auto result = std::filesystem::weakly_canonical(std::filesystem::u8path(path), errorCode);
  return !errorCode ? std::make_optional(std::move(result)) : std::nullopt;
}

补充说明

  • 如果Path是轻量类型(仅包含栈上成员),拷贝和移动开销几乎一致,此时加不加std::move区别不大,但添加也不会带来负面影响。
  • 若Path的移动构造函数标记为noexcept,std::optional在构造时会利用这一点,避免额外的异常安全检查,进一步提升性能。

内容的提问来源于stack exchange,提问作者Jiří Lechner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:50:26