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

为何不始终采用右值引用作为C++ API的参数?

回调API的参数传递设计:右值引用 vs 按值传递

问题背景

我维护着一个API,功能是将订阅项加入存储std::function的vector中。由于std::function可能涉及堆内存分配,对其执行move操作是合理且高效的。目前这个API有两种实现方式:

  • 按值传递参数
  • 按右值引用传递参数

场景分析

在实际使用中,用户传入lambda(右值)的概率极高:

  • 若采用按值传递,函数体内会额外触发一次std::function的move构造,这属于不必要的性能开销;
  • 若采用右值引用传递,虽然要求用户传入右值,但如果用户需要将同一个函数对象分发到多个接收端,只需在调用API前自行拷贝对象即可。

核心疑问

既然如此,为什么不始终选择第二种设计——用右值引用作为参数,让调用者自行处理拷贝操作?这种设计不会给用户带来额外负担:仅在需要复用对象时提前拷贝,其他场景下用户完全能掌控API的行为,还能避免move构造带来的性能损耗。


示例代码

#include <functional>
#include <cstdio>
#include <vector>


struct entity
{
    auto on_change(std::function<void()>&& cb)
    {
        callbacks_.push_back(std::move(cb));
    }

    auto print_all() {
        std::for_each(callbacks_.cbegin(), callbacks_.cend(), [](const auto& cb){ cb(); });
    }

    std::vector<std::function<void()>> callbacks_;
};

auto foo() -> void {
    printf("Hello from function!\n");
}

int main()
{
    entity e,d;

    // 常规场景:直接传入右值lambda
    e.on_change([](){ printf("Hello from lambda!\n"); });

    // 特殊场景:需要复用同一个函数对象
    auto fn = std::function<void()>([]{ printf("Hello from function that gets distributed multiple times!\n"); });
    // 以下两行无法编译,因为fn是左值
    // e.on_change(fn); 
    // d.on_change(fn);
    // 可通过以下方式处理:
    e.on_change(std::function<void()>(fn)); // 显式拷贝新的function对象
    d.on_change(std::move(fn)); // 转移fn的所有权给d的回调列表

    e.print_all();
    d.print_all();
}

程序输出

Hello from lambda!
Hello from function that gets distributed multiple times!
Hello from function that gets distributed multiple times!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:05:21