为什么在C++20中要使用`std::bind_front`而不是lambda表达式?

65

如同一个类似措辞的问题所提到的 (为什么在c++14中使用std::bind而不是lambda表达式?),答案是——没有理由(并且还提到了为什么使用lambda更好)。

我的问题是——如果在C++14中已经不需要使用bind了,为什么标准委员会觉得有必要在C++20中添加std::bind_front呢?

现在它是否有任何新优势可以胜过lambda?

2个回答

64

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版本更加简洁,只需命名三个事物:bind_front,要调用的成员函数以及将要呼叫该函数的实例。这也是我们目前需要的:标记表示我们正在创建一个函数的第一个参数绑定,被绑定的函数和我们想要绑定的参数。没有多余的语法或其他细节。

相比之下,lambda有很多我们在此情况下不关心的东西。例如auto... argsstd::forward等。它的功能有点难以理解,而且阅读起来要长得多。

请注意,bind_front根本不允许使用bind的占位符,因此它并不真正是一个替代品。它更像是bind最有用形式的简写。


8
值得注意的是,使用 bind_front 带来了优化器的好处——它比使用带有各种花哨功能的 bind 更加简单和受限。这样实现出来的代码要小得多,优化器可以更容易地进行优化。 - chris

46
提出该概念的论文《简化部分函数应用》提出了一些好的、有说服力的用例。我将在此进行总结,因为否则我就必须引用大部分论文,所以请务必查看一下:

自动完美转发

使用lambda会涉及到std::forward的样板文件。

传播可变性

在按值存储对象的情况下,std::bindstd::bind_front传播constness,但在捕获lambda的情况下,用户必须选择创建问题的可变或不可变版本。

保留返回类型

使用lambda会在用户端涉及到-> decltype(auto)的样板文件。

保留值类别

与保留可变性相似,只是现在我们讨论的是lvalue/rvalue,并且只有std::bind_front可以正确地处理它们。

支持一次性调用

这是传播可变性和保留值类别的结果。

保留异常规范

现在异常规范已成为类型系统的一部分,这尤其重要。


cppreference也有一些有用的注释:

该函数旨在替换std::bind。与std::bind不同,它不支持任意参数重新排列,也没有针对嵌套的绑定表达式或std::reference_wrappers的特殊处理。另一方面,它关注调用包装对象的值类别,并且传播底层调用运算符的异常规格。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接