泛型代码中处理可调用对象:优先选完美转发还是拷贝构造并移动?
优先选择完美转发的实现(transform_pf)
先看给出的两个辅助函数实现:
#include <algorithm> #include <span> #include <vector> template <typename T,typename Transform> auto transform_cm(std::span<T> spn, Transform function) -> std::span<T> { std::transform( spn.begin(), spn.end(), spn.begin(), std::move(function) ); return spn; } template <typename T,typename Transform> auto transform_pf(std::span<T> spn, Transform&& function) -> std::span<T> { std::transform( spn.begin(), spn.end(), spn.begin(), std::forward<Transform>(function) ); return spn; }
在泛型代码处理可调用对象时,优先选完美转发的transform_pf,理由如下:
避免不必要的拷贝开销:
transform_cm的参数是按值传递,无论传入左值还是右值,都会先对可调用对象做一次拷贝构造。如果可调用对象捕获了大内存的变量(比如大型std::vector),这次拷贝会带来明显的性能损耗。哪怕之后用std::move把拷贝后的对象传给std::transform,也无法消除最初拷贝的成本。保留值类别语义:
transform_pf通过右值引用参数配合std::forward实现完美转发,能完全保留传入对象的原始值类别:- 若传入临时对象(比如匿名lambda),会以右值形式传递给
std::transform,直接使用临时对象,无额外拷贝; - 若传入左值(比如已命名的lambda变量),会以左值引用形式传递,符合调用者可能重复使用该对象的预期。
- 若传入临时对象(比如匿名lambda),会以右值形式传递给
符合泛型代码的零开销原则:完美转发是C++泛型编程中实现“零抽象开销”的核心手段之一,在没有特殊限制的情况下,它能让泛型函数的行为更贴近直接调用底层函数的效率和语义。
只有当你明确需要在函数内部保留可调用对象的独立副本时,才需要考虑transform_cm这种拷贝后移动的方式。而在这个场景下,我们只是把可调用对象传递给std::transform使用,完美转发显然是更优的选择。
内容的提问来源于stack exchange,提问作者milad lashini
相关产品推荐
相关产品推荐

