clang-tidy的modernize-pass-by-value建议仅适用于C++20及以上吗?
C按值传递并移动的性能变化:C20的关键改进
核心结论
clang-tidy的modernize-pass-by-value建议并非仅在C20及以上才"正确",但**在C20及以上,该建议对左值(lvalue)和右值(rvalue)都能保证最优性能**;而在C++17及更早版本中,按值传递并移动确实会给左值带来额外的性能开销。
C++17中按值传递左值的性能损耗原因
当传递const左值给按值传递的构造函数时(比如示例中的PassByValueAndMove),C++17及之前的编译器必须执行两步操作:
- 先拷贝构造函数的参数(从传入的const左值);
- 再移动构造类的成员变量(从函数参数)。
虽然移动操作的开销极小,但编译器无法消除中间的参数对象——因为C++17的强制拷贝消除仅适用于纯右值(prvalue,比如临时对象),命名的函数参数属于泛左值(glvalue),无法被省略。相比之下,按const引用传递的构造函数(PassByReferenceAndCopy)只需要直接拷贝构造成员变量,少了中间对象的构造/析构成本,因此在基准测试中表现更快。
C++20的关键优化:消除按值传递左值的额外开销
C++20放宽了拷贝消除的限制,允许编译器将函数参数的拷贝构造与成员的移动构造合并。具体来说,当函数参数被立即用于移动构造类成员时,编译器可以直接将实参的内容拷贝到成员变量的内存位置,完全省略中间的参数对象。这使得:
- 按值传递并移动的代码路径,与按const引用传递的代码路径在性能上完全一致;
- 同时保留了对右值的高效支持(直接移动,无需额外重载)。
这种优化由编译器自动实现,无需开发者做任何额外操作,因此在C++20及以上的基准测试中,BM_PassLvalueByValueAndMove的性能与BM_PassLvalueByReferenceAndCopy完全持平。
基准测试代码
#include <benchmark/benchmark.h> #include <string> #include <utility> struct PassByValueAndMove { explicit PassByValueAndMove(std::string string) : m_string(std::move(string)) { } std::string m_string; }; struct PassByReferenceAndCopy { explicit PassByReferenceAndCopy(const std::string& string) : m_string(string) { } std::string m_string; }; struct PassBoth { explicit PassBoth(std::string&& string) : m_string(std::move(string)) { } explicit PassBoth(const std::string& string) : m_string(string) { } std::string m_string; }; static void BM_PassRvalueByReferenceAndCopy(benchmark::State& state) { for (auto _ : state) { static_cast<void>(_); { PassByReferenceAndCopy instance("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); benchmark::DoNotOptimize(instance); } } } BENCHMARK(BM_PassRvalueByReferenceAndCopy); static void BM_PassRvalueByValueAndMove(benchmark::State& state) { for (auto _ : state) { static_cast<void>(_); { PassByValueAndMove instance("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); benchmark::DoNotOptimize(instance); } } } BENCHMARK(BM_PassRvalueByValueAndMove); static void BM_PassLvalueByReferenceAndCopy(benchmark::State& state) { for (auto _ : state) { static_cast<void>(_); { const std::string dummy_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; PassByReferenceAndCopy instance(dummy_string); benchmark::DoNotOptimize(instance); } } } BENCHMARK(BM_PassLvalueByReferenceAndCopy); static void BM_PassLvalueByValueAndMove(benchmark::State& state) { for (auto _ : state) { static_cast<void>(_); { const std::string dummy_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; PassByValueAndMove instance(dummy_string); benchmark::DoNotOptimize(instance); } } } BENCHMARK(BM_PassLvalueByValueAndMove); static void BM_PassRvalueToStructOfferingBoth(benchmark::State& state) { for (auto _ : state) { static_cast<void>(_); { PassBoth instance("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); benchmark::DoNotOptimize(instance); } } } BENCHMARK(BM_PassRvalueToStructOfferingBoth); static void BM_PassLValueToStructOfferingBoth(benchmark::State& state) { for (auto _ : state) { static_cast<void>(_); { const std::string dummy_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; PassBoth instance(dummy_string); benchmark::DoNotOptimize(instance); } } } BENCHMARK(BM_PassLValueToStructOfferingBoth);
内容的提问来源于stack exchange,提问作者Benjamin Bihler
相关产品推荐
相关产品推荐

