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

为何std::function_ref允许传入将亡值仿函数而非直接禁止?

std::function_ref为何不禁止右值引用参数?

在cppreference的std::function_ref文档中,存在如下重载构造函数:

template<class F>
function_ref(F&& f) noexcept;

该构造函数通过std::addressof(f)初始化绑定实体,但允许调用者传入将亡值仿函数,进而导致使用悬垂引用调用已销毁的仿函数,示例代码如下:

#include <functional>

std::function_ref<int(int)> f() {
    auto       n  = 1;
    auto const fn = [n](int m) { return n + m; };
    return std::function_ref<int(int)>(std::move(fn)); // 匹配该构造函数
}

int main() {
    auto fn_ref = f();
    // fn_ref现在引用已销毁的仿函数!
    return fn_ref(2); // 崩溃!
}

而std::ref与std::cref针对此类问题做了更安全的设计,通过删除右值引用重载来避免风险:

template<class T>
void ref(const T&&) = delete;

template<class T>
void cref(const T&&) = delete;

疑问:为何C++标准不直接拒绝右值引用(包括&&和const&&)类型的仿函数参数?


回答

  • 支持临时函数对象的合法使用场景:有些临时函数对象的生命周期足够覆盖function_ref的使用周期,比如在同一表达式内直接调用:

    // 合法场景:临时lambda的生命周期到表达式结束,调用时对象仍存在
    int result = std::function_ref<int(int)>([](int x){return x*2;})(5);
    

    如果直接禁止右值参数,这类合法的便捷用法也会被阻断。

  • 灵活性与安全性的权衡:function_ref的设计初衷是作为轻量级的函数引用包装,追求极致灵活和零开销。标准委员会选择保留右值重载,是为了不限制用户在明确知晓生命周期安全的情况下使用临时对象——毕竟function_ref本身就是为能确保被引用对象生命周期长于包装器的场景设计的,逻辑类似原始引用,把生命周期管理的责任交给用户。

  • 与std::ref/cref的设计目标差异:std::ref和std::cref的核心目的是在需要传递引用的场景(比如std::bind或标准算法)中避免对象拷贝,它们的使用场景偏向“绑定已有对象的引用”,而非接受临时对象,所以删除右值重载符合其定位。但function_ref的定位更宽泛,既可以绑定左值,也允许在安全前提下绑定临时对象,因此不能照搬ref/cref的限制。

  • 用户可自行规避风险:如果需要强制避免右值绑定,用户可以通过封装、静态断言或C++20的概念约束来限制function_ref的构造参数为左值。标准没有强制禁止,是为了给用户留下更多灵活选择的空间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 21:05:00