为什么在 C++20 中使用 std::bind_front` 而不是 lambda?
Why use `std::bind_front` over lambdas in C++20?
正如措辞类似的问题 (Why use bind over lambdas in c++14?) 中提到的那样,答案是 - 没有理由(并且还提到了为什么使用 lambda 会更好)。
我的问题是 - 如果在 C++14 中不再有使用绑定的理由,为什么标准委员会发现有必要在 C++20 中添加 std::bind_front
?
它现在与 lambda 相比有什么新优势吗?
bind_front
绑定第一个 X 参数,但如果可调用函数调用更多参数,它们会被附加到最后。当您仅绑定函数的前几个参数时,这使得 bind_front
非常可读。
最明显的例子是为绑定到特定实例的成员函数创建可调用对象:
type *instance = ...;
//lambda
auto func = [instance](auto &&... args) -> decltype(auto) {return instance->function(std::forward<decltype(args)>(args)...);}
//bind
auto func = std::bind_front(&type::function, instance);
bind_front
版本噪音小 很多。它说到点子上了,恰好有 3 个命名的东西:bind_front
,要调用的成员函数,以及将在其上调用它的实例。这就是我们的情况所需要的:一个标记来表示我们正在创建函数的第一个参数的绑定、要绑定的函数和我们想要绑定的参数。没有多余的语法或其他细节。
相比之下,lambda 在这个位置有很多我们不关心的东西。 auto... args
位,std::forward
等等。弄清楚它在做什么有点困难,而且阅读起来肯定要长得多。
请注意 bind_front
根本不允许使用 bind
的占位符,因此它并不是真正的替代品。它更像是 shorthand 最有用的形式 bind
。
提出它的论文 Simplified partial function application 有一些很好的引人注目的用例。我将在这里总结一下,否则我将不得不引用大部分论文,所以一定要去看看:
自动完美转发
使用 lambda 将涉及 std::forward
样板文件
传播可变性
在按值存储对象的情况下 std::bind
和 std::bind_front
传播常量,但在捕获 lambda 的情况下,用户必须选择可变或常量版本,这会产生问题
保留 return 类型
使用 lambda 将涉及 -> decltype(auto)
用户端的样板文件。
保值类
就像保留可变性一样,除了现在我们正在谈论 lvalue/rvalue 并且只有 std::bind_front
正确地做到了这一点
支持一次性调用
传播可变性和保留价值类别的结果
保留异常规范
这现在尤其重要,因为异常规范现在是类型系统的一部分
cppreference 也有一些有用的注释:
This function is intended to replace std::bind. Unlike std::bind, it
does not support arbitrary argument rearrangement and has no special
treatment for nested bind-expressions or std::reference_wrappers. On
the other hand, it pays attention to the value category of the call
wrapper object and propagates exception specification of the
underlying call operator.
正如措辞类似的问题 (Why use bind over lambdas in c++14?) 中提到的那样,答案是 - 没有理由(并且还提到了为什么使用 lambda 会更好)。
我的问题是 - 如果在 C++14 中不再有使用绑定的理由,为什么标准委员会发现有必要在 C++20 中添加 std::bind_front
?
它现在与 lambda 相比有什么新优势吗?
bind_front
绑定第一个 X 参数,但如果可调用函数调用更多参数,它们会被附加到最后。当您仅绑定函数的前几个参数时,这使得 bind_front
非常可读。
最明显的例子是为绑定到特定实例的成员函数创建可调用对象:
type *instance = ...;
//lambda
auto func = [instance](auto &&... args) -> decltype(auto) {return instance->function(std::forward<decltype(args)>(args)...);}
//bind
auto func = std::bind_front(&type::function, instance);
bind_front
版本噪音小 很多。它说到点子上了,恰好有 3 个命名的东西:bind_front
,要调用的成员函数,以及将在其上调用它的实例。这就是我们的情况所需要的:一个标记来表示我们正在创建函数的第一个参数的绑定、要绑定的函数和我们想要绑定的参数。没有多余的语法或其他细节。
相比之下,lambda 在这个位置有很多我们不关心的东西。 auto... args
位,std::forward
等等。弄清楚它在做什么有点困难,而且阅读起来肯定要长得多。
请注意 bind_front
根本不允许使用 bind
的占位符,因此它并不是真正的替代品。它更像是 shorthand 最有用的形式 bind
。
提出它的论文 Simplified partial function application 有一些很好的引人注目的用例。我将在这里总结一下,否则我将不得不引用大部分论文,所以一定要去看看:
自动完美转发
使用 lambda 将涉及 std::forward
样板文件
传播可变性
在按值存储对象的情况下 std::bind
和 std::bind_front
传播常量,但在捕获 lambda 的情况下,用户必须选择可变或常量版本,这会产生问题
保留 return 类型
使用 lambda 将涉及 -> decltype(auto)
用户端的样板文件。
保值类
就像保留可变性一样,除了现在我们正在谈论 lvalue/rvalue 并且只有 std::bind_front
正确地做到了这一点
支持一次性调用
传播可变性和保留价值类别的结果
保留异常规范
这现在尤其重要,因为异常规范现在是类型系统的一部分
cppreference 也有一些有用的注释:
This function is intended to replace std::bind. Unlike std::bind, it does not support arbitrary argument rearrangement and has no special treatment for nested bind-expressions or std::reference_wrappers. On the other hand, it pays attention to the value category of the call wrapper object and propagates exception specification of the underlying call operator.