C++ 中不使用 std::move 的移动

Hacker News Top 新闻

摘要

本文解释了 C++23 如何在特定情况下允许隐式移动,减少对 std::move 的需求,并通过返回值优化和拷贝省略提升性能。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/09/02 14:53

# 不使用std::move的C++移动语义 来源:https://andreasfertig.com/blog/2026/09/move-in-cpp-without-a-stdmove/ 在我之前的一篇文章《为何应极少使用std::move》(https://andreasfertig.com/blog/2022/02/why-you-should-use-stdmove-only-rarely/)中,我曾建议应尽量减少使用`std::move`。今天我想分享这条建议带来的好处:默认情况下就能获得最佳性能。 ## 返回值优化 性能最大的敌人之一就是不必要的拷贝操作。 你一定听说过返回值优化(RVO)。只要条件允许,我们都应力求实现RVO。RVO意味着返回的对象不会在函数内部的栈上创建,而是在最终会使用该值的调用侧构造。这能避免拷贝和移动操作。标准规范中称之为"拷贝消除",因为标准从不直接讨论编译器优化。 自C++17起,在以下情况下保证实现拷贝消除: ``` Apple RVO() { return {}; } ``` 这是纯粹的RVO。接着是命名返回值优化(NRVO): ``` Apple NRVO() { Apple res{}; return res; } ``` 后者不在保证性拷贝消除的范围内。不过实际执行时通常也不会产生拷贝或移动开销。 ## 移动替代拷贝 仅次于拷贝消除的最佳选择是移动对象。自C++11起,语言中已有几处自动发生隐式移动的场景: ``` Apple Fun(Apple val) { return val; } ``` 这段代码中,结果对象将从参数进行移动构造。 但语言中存在一些更复杂的情况。 首先想分享的是这个例子: ``` Apple Cat(Apple&& val) { return val; } ``` 该函数接受右值引用参数并返回刚接收的对象。虽然代码能编译通过,但在C++20之前,返回值将进行拷贝构造。嗯,前提是使用符合标准的编译器如GCC。而规范遵从度较低的编译器Clang却会给出移动构造。确实,有时不墨守成规反而效果更好。 另一个我更不喜欢的例子如下: ``` Apple Cat(Apple&& val) { return val; } ``` 同样是接受右值引用参数的函数,但这次返回的是右值引用。此时若不手动移动返回值,代码将无法编译。这与我的建议相悖。 公平地说,要在这两种情况下获得最佳效果,都需要对返回值进行移动操作。因此两者都违背了我之前的建议。 当将编译器切换至C++23模式后,这两种情况都会自动进行隐式移动。无需使用`std::move`!现在你可以更彻底地遵循我"极少使用`std::move`"的原则了! Andreas

相似文章

C++ 中的惰性初始化

Lobsters Hottest

本文解释 C++ 中的惰性初始化,通过将计算推迟到必要时来优化性能,使用辅助结构体来利用 std::optional 中的转换运算符。

const_cast:必要之恶

Lobsters Hottest

本文解释了为什么在C++中有时必须使用const_cast,特别是从std::priority_queue中移动对象时,以及如何安全地使用它。

信任你的编译器:现代C++

Hacker News Top

本文对比了旧的C++性能技巧与现代编译器的能力,表明编译器现在能够将朴素代码优化得比手工调整的技巧更好。包含在AMD Zen 5上使用Clang 21的基准测试。