CppCon 2017 学习:folly::Function A Non-copyable Alternative to std::function

你说的内容是关于 C++ 中 可调用类型(Callable Types) 的基础知识,我帮你理清并补充理解。

Callable Types(可调用类型)简介

C++ 中任何可以用 () 括号操作符"调用"的对象,都叫做 可调用类型。典型包括:

1. 函数指针和函数引用

cpp 复制代码
int (*functionPointer)(std::string);   // 指向函数的指针
int (&functionReference)(std::string); // 函数的引用

它们都指向一个可被调用的函数,调用方式如:

cpp 复制代码
int result = functionPointer("hello");
int result2 = functionReference("world");

2. Lambda 表达式

匿名函数对象,语法:

cpp 复制代码
auto lambda = [](std::string s) -> int { return s.size(); };
int result = lambda("test");

lambda 实际上是编译器生成的匿名类对象,重载了 operator()

3. 函数对象(Functor)

你可以自定义一个类或结构体,实现 operator(),使其实例可以像函数一样被调用:

cpp 复制代码
class ComplexObject {
public:
    int operator()(std::string s) {
        return s.size() * 2;  // 举例
    }
};
ComplexObject obj;
int result = obj("hello");

4. 补充:static operator()

注意,你提到的:

cpp 复制代码
class ComplexObject {
  static int operator()(std::string);
};

这个声明是不合法的。因为 operator() 必须是实例成员函数(non-static),它的意义就是让对象可以被调用。

如果你写 static,就不能通过对象调用了:

cpp 复制代码
ComplexObject obj;
obj("hello");  // 不行,static operator() 无法用obj调用

你可以写普通静态成员函数,但那就不是函数调用运算符重载了。

小结

类型 示例 说明
函数指针 int (*fp)(std::string); 指向普通函数
函数引用 int (&fr)(std::string); 绑定到函数
Lambda 表达式 auto lambda = [](std::string) { ... }; 匿名函数对象,重载了 operator()
函数对象(重载 operator() class Foo { int operator()(std::string); }; 自定义可调用类型

带状态的可调用对象(Stateful Callables),让我帮你详细解释和梳理下:

Stateful Callables(有状态的可调用对象)

"有状态"指的是这个可调用对象内部保存了数据(状态),每次调用可能用到这些状态或改变它们。

1. 捕获变量的 Lambda(Stateful Lambda)

cpp 复制代码
int x = 5;
auto lambda = [x](std::string s) { return s.size() + x; };
  • 这里的 lambda 捕获了外部变量 x,把它存到 lambda 对象里。
  • 每次调用 lambda,都会用这个 x 参与计算。
  • 这个 lambda 对象内部有状态(x),所以是 stateful callable

2. 自定义类或结构体,重载非静态 operator()

cpp 复制代码
class ComplexObject {
    int x;
public:
    ComplexObject(int x_) : x(x_) {}
    int operator()(std::string s) const {
        return s.size() + x;
    }
};
  • 这里 ComplexObject 有成员变量 x(状态)
  • 重载了 operator(),通过对象调用时可以用到内部状态。
  • 也是一个 stateful callable

3. 为什么 operator() 要是非静态?

  • operator() 是调用对象的核心,非静态才能访问对象的成员变量(状态)。
  • 静态 operator() 没法访问对象成员,因此不算真正的"stateful callable"。

4. 总结

类型 例子 特点
无状态 Lambda [](int x){ return x*x; } 没捕获外部变量,纯函数
有状态 Lambda [x](std::string s){ return s.size() + x; } 捕获外部变量,内部有状态
函数对象(类/结构体) 有成员变量的类重载非静态 operator() 内部保存状态,调用时可用

这部分讲的是 会修改自身状态的可调用对象(State-Mutating Callables),我帮你详细说明:

State-Mutating Callables(会改变状态的可调用对象)

这类 callable 不仅"有状态",而且在调用时会修改它们内部的状态

1. mutable lambda

默认情况下,捕获外部变量的 lambda 是 const 调用的,不能修改捕获的变量副本。

cpp 复制代码
int x = 0;
auto lambda = [x](std::string s) mutable {
    x += s.size();  // 修改了 lambda 内部捕获的 x 副本
    return x;
};
  • mutable 关键字让 lambda 的 operator() 变成非 const,允许修改捕获的变量副本。
  • 注意,捕获的是值拷贝,修改的是 lambda 内部的那个副本,不影响外部 x

2. 非 const 的非静态 operator()

cpp 复制代码
class ComplexObject {
    int x = 0;
public:
    int operator()(std::string s) {
        x += s.size();  // 修改对象的状态
        return x;
    }
};
  • 这里 operator() 不是 const,说明调用会改变对象状态。
  • 你可以记录调用次数、累计某些值等。

3. 区别于前面 const operator() 的地方

  • const operator() 不允许修改成员变量(除非用 mutable 修饰成员变量)
  • 非 const operator() 可以修改成员变量,实现状态变化

4. 总结表

类型 示例 说明
mutable lambda [x](std::string s) mutable { x += s.size(); return x; } 允许修改捕获变量的副本
类的非 const operator() int operator()(std::string s) { x += s.size(); return x; } 允许修改对象成员变量,状态改变

你提到的是传递可调用对象(callables)给函数的方式,特别是用 函数指针 传递的限制,我帮你详细解释:

传递可调用对象(Passing Callables)

1. 函数指针只能传递无状态的可调用对象

举例:

cpp 复制代码
std::string work(int x);
void workAsynchronously(int x, void (*processResult)(std::string));
  • processResult 是一个 函数指针 ,指向一个 void(std::string) 的函数。
  • 这意味着传入的回调只能是无状态的普通函数,不能是有状态的 lambda 或函数对象。

2. 为什么函数指针只能指向无状态函数?

  • 函数指针实际指向具体的函数地址
  • 有状态 lambda 和函数对象是对象实例 ,它们保存状态,需要调用其成员函数(operator()),而不是普通函数地址。
  • 因此,函数指针无法表示带状态的可调用对象。

3. 如果想传递有状态 callable,该怎么办?

  • std::function(类型擦除),支持任何可调用对象,包括有状态的 lambda 和函数对象:
cpp 复制代码
#include <functional>
void workAsynchronously(int x, std::function<void(std::string)> processResult);
  • 这样,传入的 processResult 可以是:
cpp 复制代码
auto lambda = [capturedData](std::string s) { /*...*/ };
workAsynchronously(5, lambda);

4. 总结

传递方式 支持的 callable 类型 备注
函数指针(void(*)(T) 只能无状态的普通函数 不能传递捕获变量的 lambda
std::function<T> 支持所有 callable,包括带状态的 lambda 和函数对象 灵活但有一定运行时开销

std::function 其实是个 函数包装器(Function Wrapper),用来封装各种可调用对象,提供统一调用接口。下面帮你拆解理解:

std::function 简化结构解析

cpp 复制代码
template<typename R, typename... Args>
class function<R(Args...)> {
  void* state;                        // 存储可调用对象的状态(捕获的数据)
  void (*func)(void*, Args...);      // 指向"调用"函数的指针,传入state和参数
  void (*destroy)(void*);            // 指向销毁函数,用于清理state指针内存
  ~function();                       // 析构时调用destroy清理state
  R operator()(Args... args);         // 调用包装的函数对象
  template<typename F>
  function(F&& f);                   // 构造时,把任意可调用对象包装进来
  template<typename F>
  function& operator=(F&& f);        // 赋值操作符,重新包装
};

关键点

  • void state *
    这里保存了任意类型的可调用对象的状态,比如捕获的变量、类实例等等。
    void* 实现类型擦除,隐藏了具体类型。
  • func 指针
    指向一个统一的调用接口,实现了调用时把 state 传进去,再调用对应可调用对象的 operator()
  • destroy 指针
    用于析构 state 指向的对象,防止内存泄漏。
  • 构造函数模板
    允许用任意类型的可调用对象(函数指针、lambda、函数对象)来初始化 std::function
  • 调用运算符
    std::function 实例看起来像普通函数一样,可以直接用 () 调用。

为什么需要这么设计?

  • 统一接口:不管是普通函数、带状态的 lambda,还是自定义的函数对象,都能用同一种类型变量来保存并调用。
  • 类型擦除:隐藏不同可调用对象类型的差异,简化代码调用。
  • 灵活性和便利性:把多态调用变得简单。

你提到的是 std::function 的实现细节和其运行时开销,我来帮你详细解析这些点,帮助你更好理解:

std::function 的结构(以 libstdc++ 的实现为例)

一个 std::function<R(Args...)> 实例,大约占 48 字节(在 x86_64 上),这些字节大致分配如下:

组成部分 说明
函数调用指针 指向调用封装对象的函数:R (*invoke)(void*, Args...)
管理函数指针 指向处理 copy、destroy、move 的函数:void (*manager)(...)
内部 buffer(32B) 小对象优化区,用于直接放入小对象(例如小 lambda)
或者指针指向堆内对象 如果封装对象较大,就会动态分配在堆上,buffer 存储的是指针地址

特性解析

小对象优化(SBO:Small Buffer Optimization)

  • 如果你的 lambda 或函数对象小于 32 字节,就会直接放入内部 buffer,避免堆分配,提高性能。
  • 如果太大,就会在堆上创建对象,然后内部 buffer 存放指针。

可复制(copyable)

  • std::function 是可复制的。
  • 拷贝会拷贝封装的对象(调用对应的 copy 管理函数)。
cpp 复制代码
std::function<void()> f1 = [] { std::puts("hi"); };
std::function<void()> f2 = f1;  // 拷贝了 lambda 封装体

不一定 noexcept 可移动

  • 移动操作不是 noexcept,意味着在某些容器(如 std::vector)中移动 std::function 可能触发重新分配或异常传播。
  • 原因:封装的对象类型可能自带抛异常的移动构造函数。

示例:大小对比

cpp 复制代码
#include <iostream>
#include <functional>
int main() {
    std::function<void()> small = [] { std::cout << "small\n"; };
    auto bigLambda = [buffer = std::array<int, 100>{}] { std::cout << "big\n"; };
    std::function<void()> big = bigLambda;
    std::cout << "sizeof(std::function): " << sizeof(std::function<void()>) << "\n";
}

输出(在 x86_64 上):

复制代码
sizeof(std::function): 48

总结

特性 说明
大小 通常 48 字节(libstdc++)
SBO 32 字节内的对象放栈上,否则放堆上
可复制 会复制被封装对象
移动非 noexcept 使用时注意异常安全性
功能 封装任意可调用对象,延迟调用,多态调用

你说的这段是在说明 std::function典型使用场景 ,尤其是它的任务封装功能。我来帮你逐句解释:

典型用途解释

「passing a task to libraries for execution at a later time」

将某个任务(比如 lambda)传给一个库,让它**"以后"执行**,不是现在马上执行。

例子:传一个回调到异步网络库中。

cpp 复制代码
void startAsync(std::function<void(std::string)> callback);
「or in a different thread」

把任务传到另一个线程去执行,比如线程池。

例子:

cpp 复制代码
std::thread t([] { doWork(); });
「storing those tasks in the library implementations」

这些任务(lambda、函数等)通常会被**"保存"在库内部的数据结构里**,等时间合适再执行。

例子:任务队列

cpp 复制代码
std::queue<std::function<void()>> taskQueue;
「in either case, those tasks are never executed more than once」

这些任务通常只执行一次,执行完就丢弃,没有重用的需求。

所以不需要支持多次调用,比如:

cpp 复制代码
auto task = [] { std::puts("run once"); };
task(); // 
task(); //  通常不会有这个需求
「and there is never a need to copy them」

这些任务传进去之后,不会被复制。只需要"移动"进库中,然后库调用它就结束了。

总结成一句话:

这些"任务式"的可调用对象通常:

  • 只执行一次
  • 被移动而非复制
  • 用于延迟/异步执行
  • 常用于线程池、事件循环、任务调度器中

延伸建议:使用 std::function 还是 std::movestd::unique_function

如果任务只用一次、无需复制,std::function 有点重。C++23 起可以用 std::move_only_function(或者第三方的 unique_function),更轻更高效:

cpp 复制代码
std::move_only_function<void()> task = std::move(lambda);

你这段是对 Facebook 的开源代码库(如 Folly)中,std::function典型使用场景的描述。我来帮你逐条解释:

内容逐句解析

folly::Executor* executor;

这是一种抽象接口指针,用来表示一个"任务执行器"。

executor->add(callable);

往这个执行器里"添加"一个任务(可调用对象)。callable 可以是 lambda、函数、绑定对象等。

folly::Executor is an interface (abstract base class) for passing tasks that need to be executed

folly::Executor 是一个接口类(抽象基类) ,它的职责是:接收并执行异步任务

相当于你设计了一个标准协议,任何执行器(线程池、事件循环等)都可以实现它。

implementations include a thread pool which executes tasks in parallel

这个接口的具体实现包括:

  • 线程池(并行执行)
  • 单线程事件循环(串行调度)
  • IO 线程(和 Reactor 模式结合)
std::function<void()> was used to pass tasks to the executor

std::function<void()> 是传给 executor->add() 的参数类型,表示:

"一个可以执行、不带参数、不返回值的任务"。

这种使用方式让 folly::Executor 成为一个高层、通用的任务调度器接口。

总结

Facebook/Folly 的实践中

元素 作用
folly::Executor 任务执行接口(类似抽象线程池)
executor->add(task) 添加一个延迟执行的任务
std::function<void()> 用来封装传入的任务

设计上的好处

  • 任务的来源不受限制(lambda、函数、类)
  • 执行方式可以灵活替换(线程池、主线程调度器)
  • 接口通用、便于解耦模块

这段讲的是 Facebook Folly 库中 Future 的常见使用场景 ,以及它与 std::function 的关系。我来帮你逐句解析 + 总结背后原理

示例代码含义

cpp 复制代码
folly::Future<std::string> result = someRpcCall(1, 2, 3);

表示调用了一个异步 RPC 函数 ,它返回一个 Future<std::string>,未来会获得一个 std::string

cpp 复制代码
result.then([&foo](std::string r) {
    return foo.extractNumber(r);
})

给这个 Future 添加一个 .then() 回调:

  • result 可用了,就调用 lambda。
  • lambda 从字符串里提取出数字(int)。
cpp 复制代码
.then([obj = std::move(obj)](int x) {
    obj.setNumber(x);
});

接着链式 .then() 调用,把前一个结果 x(一个 int)交给另一个 lambda。

  • 这里用了 C++ 的"带 move capture 的 lambda"
  • obj 是只在 lambda 中使用的一个局部状态对象

概念解释:Future + then 回调机制

Future<T>:代表未来会得到一个 T

这是一个异步结果占位符

.then(func)

当结果可用时,调用你传进去的 func(回调)

就像 JavaScript 的 .then(),但类型安全,支持 C++ 特性。

回调的存储方式:使用 std::function

你写的这句:

"the implementation used to use std::function to store the callback"

表示早期实现是这样写的:

cpp 复制代码
std::function<void(T)> callback;

也就是用 std::function 来存储 then() 传入的 lambda。这有几个好处:

优点 缺点
可以存储任意可调用对象 比较重(拷贝 / 类型擦除)
简化了接口 不支持 move-only 类型

后续优化:不再使用 std::function

因为 std::function 不支持:

  • move-only lambda(捕获 unique_ptr 或 std::move(obj))
  • noexcept move
  • 精确类型推导(性能)
    所以 Folly 后来换成了 手写的轻量 type-erased function wrapper,支持 move-only 语义。类似于:
cpp 复制代码
template<typename T>
struct MoveOnlyCallback {
  virtual void operator()(T) = 0;
  virtual ~MoveOnlyCallback() = default;
};

总结

这段代码展示了:

内容 意义
Future<T> 管理异步结果
.then(callback) 注册异步回调
回调传 lambda(可带状态) 支持链式操作、异步数据流
早期用 std::function 储存回调 简单但不支持 move-only,后来被优化掉

你这段是讲 C++ 中使用 std::function限制 ,尤其是它不支持捕获 move-only 类型 的问题,以及 Facebook Folly 提供的一些 解决方法。下面是逐点讲解与深入理解:

问题:std::function 不支持 move-only 捕获

示例问题代码:

cpp 复制代码
MoveOnlyType x;
executor.add([x = std::move(x)]() mutable { x.doStuff(); });

这在某些实现中无法编译,原因是:

std::function 只能封装可复制(copyable)对象 。而这个 lambda 捕获了 MoveOnlyType,它不可复制。

常见 Workaround 1:用 std::shared_ptr

cpp 复制代码
auto x = std::make_shared<MoveOnlyType>();
executor.add([x]() { x->doStuff(); });

优点:

  • lambda 可复制了,shared_ptr 也是可复制的

缺点:

  • 每次调用都涉及堆分配
  • 需要原子操作维护引用计数(性能差)
  • 会让你为避免 std::function 限制而牺牲所有权语义

Workaround 2:用 folly::MoveWrapper<T>

cpp 复制代码
folly::MoveWrapper<MoveOnlyType> x;
executor.add([x]() mutable { x->doStuff(); });

优点:

  • MoveWrapper<T> 实际上是一个**"伪复制对象"**,在复制时会自动移动内部对象
  • 这让 lambda 看起来是可复制的,但实际上把资源从左值转成了右值传进来

缺点:

  • 违反了 C++ 的复制语义(拷贝其实是 move)
  • 很像已经弃用的 std::auto_ptr ------ 危险、易错
  • 一不小心就可能在拷贝时丢失数据

本质问题总结:

项目 问题
std::function 不能 wrap move-only lambda
lambda 捕获 move-only 会使 lambda 本身不可复制
std::function 要求 copyable 所以编译报错
workaround(shared_ptr / MoveWrapper) 都是权衡性能或语义的方案

Folly 的真正解决方案(后续)

Folly 的后续优化是引入了一个可以支持 move-only lambda 的轻量函数包装器(非 std::function),其特性包括:

  • 支持 unique_ptrPromise 等 move-only 类型
  • 支持 noexcept move
  • 避免拷贝构造限制
  • 可以零堆分配(small buffer optimization)

小结

方法 优点 缺点
std::function 通用、简洁 不支持 move-only 捕获
shared_ptr 兼容 std::function 堆分配、性能差、共享所有权
folly::MoveWrapper 可变通使用 破坏复制语义、易出错
自定义轻量函数包装器(如 Folly) 真正解决 move-only 问题 实现复杂,不是标准库

为什么需要一种不同的 Function Wrapper

std::function 的核心问题:

要求所有可调用对象(callables)是可拷贝的(copyable)

这对很多实际用例来说,是一种不必要的限制,尤其是:

我们并不需要拷贝这些可调用对象

  • 比如在线程池中提交任务时:
    • 只会执行一次(one-shot)
    • 不需要拷贝(只需要 move 进去,然后调用)
    • 可调用对象中经常有 unique_ptrPromise 这类 move-only 类型

结果是:

不能直接使用 lambda 捕获 move-only 对象

cpp 复制代码
MoveOnlyType x;
executor.add([x = std::move(x)]() mutable { x.doStuff(); }); //  std::function 不接受
  • 捕获了 MoveOnlyType,lambda 本身就变成 move-only
  • std::function<void()> 要求构造函数参数是 copyable

所以你真正想要的是:

一个轻量级的 function wrapper,可以 wrap:

  • move-only 的 lambda
  • 只 move、不 copy
  • 小对象无需堆分配
  • 只执行一次(one-shot callable)也没问题

Facebook 的 Folly 库就因此创造了:

  • folly::Function:是 move-only 的 function wrapper
  • 用于线程池、异步任务、promise 等现代用例
  • 避免了不必要的性能开销

小结:

标准库 std::function 实际开发常见需求
需要可复制 callable 只需要 move、执行一次
可能导致堆分配 想要 small buffer 或 zero allocation
不支持 move-only 捕获 现代 C++ 任务常用 move-only 对象
如果你要构建一个支持 MoveOnly 的任务系统,那使用 std::function 是不合适的。你应该考虑:
  • 自己实现一个简易的 MoveFunction
  • 或者使用 Folly 的 folly::Function

关于 const 正确性(Const Correctness)在 std::function 中的一个重要细节

你可能会以为:

cpp 复制代码
std::function<void()> f;
void someFunction(const std::function<void()>& f) {
    f(); // f 是 const,调用 f() 应该不会修改内部状态
}

表面上看,f()const 的成员函数,所以你以为

  • 包装的 lambda 或函数对象也应该是 const 调用
  • 内部状态不会被修改

实际上:

cpp 复制代码
R operator()(Args...) const;

这个 operator()const 没错,但是

  • 它内部调用的是 (*callable)(args...),这个 callable 是 void* 转型来的
  • 没有检查实际 wrapped 对象的 constness!

所以问题来了:

cpp 复制代码
std::function<void()> f = [x = 0]() mutable { /* 修改 x */ };
f(); //  可以正常调用,虽然是 const 对象

即使 fconst,只要里面包装的是 mutable lambda 或非 const operator() 的对象:

仍然可以修改状态!

总结就是:

看起来 实际上
operator()const 但 wrapped callable 可能是非-const 的
你以为不改状态 实际上可以修改状态(比如 mutable lambda)

所以这段话的重点理解是:

std::function::operator()const,但它没有真正地保证 const-correctness。
你不能依赖它来保证你的代码不会修改内部状态。

你这段内容是讲解 folly::Function 的设计目的和内部实现机制,下面是逐点的理解与总结

folly::Function 的设计动机与特点

1. Non-copyable(不可拷贝)

  • 为什么?
    要支持捕获 unique_ptrPromisemove-only 的类型
  • std::function 的问题: 只支持可拷贝类型
  • folly::Function 的解决方案:
    自己就是不可拷贝的(copy constructor & copy assignment 被删除)
    folly::Function 使用 值语义(value semantics) ,但通过 move 实现转移所有权

2. noexcept-Movable

  • 如果你定义了一个类型含有 folly::Function 成员变量,
    STL 要求你这个类型如果想 noexcept move,那成员也必须是 noexcept move
  • 所以 folly::Function 本身实现了 noexcept move,以便:
    支持 std::move_if_noexcept
    与 STL 容器、线程池等类型协作良好

3. Const Correct

  • folly::Function 有两个版本:

    cpp 复制代码
    folly::Function<void()>         // 非 const operator()
    folly::Function<void() const>   // const operator()
  • 保证 const-correctness,解决 std::function 的历史问题

  • 如果你用的是:

    cpp 复制代码
    const folly::Function<void()>& f;
    f(); // 调用的是 const operator()

    那里面包的 callable 也必须支持 const 调用!

4. 实现细节(Implementation Details)

特性 说明
大小 64 bytes(x86_64 架构)
调用指针 1 个指针:真正的调用函数
管理函数指针 用于 move、destroy 等操作
小对象优化 提供 48 字节的 inline 存储空间
大对象策略 不满足 noexcept-movable 的对象将 heap 分配

哪些对象不能 inline 存储?

  • noexcept movable 的对象
    会退化为堆分配(使用指针 + heap 管理)

设计目标小结:

目标 实现方式
支持 move-only 对象 类型本身不可拷贝
高性能 小对象优化,避免堆分配
STL 容器兼容性 noexcept move
保证 const 正确性 两个签名:void() vs void() const

这部分内容总结了 folly::Function 相比 std::function 的优势、迁移实践、适用场景以及性能表现。下面是对这些内容的逐条解释与理解

Trivia:互操作性

  • std::function 可以转换为 folly::Function(拷贝构造)
  • folly::Function 不能转换为 std::function,因为 std::function 需要拷贝构造,而 folly::Function 是不可拷贝的

迁移到 folly::Function

大多数情况下可作为 drop-in 替代品

但也有例外:

  1. 如果代码 依赖拷贝语义(很少见)
  2. 如果代码 依赖不正确的 const 使用 (那是个 bug,folly::Function 会强制你修)

使用方式差异:

  • std::function 常常以 const& 传参
    folly::Function 不可拷贝,必须以 &&& 传参

Facebook 内部采用

  • folly::Future 中替换了 std::function
    带来最大受益:可使用 move-only 的回调
  • folly::Executor 中替换 std::function
    需要修改很多子类,但修改过程常揭示原有代码问题

何时使用 std::function vs folly::Function

使用场景 推荐
需要复制 callable 的 API std::function
一般回调/异步任务/只需移动语义 folly::Function
使用 MoveWrapper 等拷贝模拟方式 不要这样做 ,使用 folly::Function 替代更安全清晰

Benchmark(性能)

调用方式 时间(越小越好) 每秒调用次数(越高越好)
函数指针调用 (fn_ptr) ~1.3 ns ~761M 次
std::function 调用 ~2.28 ns ~437M 次
folly::Function 调用 ~1.96 ns ~510M 次
std::function 创建 + 调用 ~3.04 ns ~329M 次
folly::Function 创建 + 调用 ~2.79 ns ~359M 次
folly::Function 通常快于 std::function

总结

内容
目标 替代 std::function,支持 move-only 类型
不支持 拷贝(故不能做 const& 参数)
迁移建议 改成 &&& 传参
效果 避免 ugly workarounds(如 MoveWrappershared_ptr 等)
性能 std::function 持平或更快
应用 在 Facebook 内部已广泛部署
相关推荐
猷咪8 分钟前
C++基础
开发语言·c++
IT·小灰灰10 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧11 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q12 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳012 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾12 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB15 分钟前
WideCharToMultiByte与T2A
c++
2601_9498683616 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计30 分钟前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识
蒹葭玉树41 分钟前
【C++上岸】C++常见面试题目--操作系统篇(第二十八期)
linux·c++·面试