std::bind 的功能组合

Functional composition with std::bind

我正在阅读 Nicolai Josuttis 关于 C++11 STL 的美丽书籍的第二版。

我找到了下面这段代码:

#include <functional>
#include <iostream>

int main()
{
   auto plus10 = std::bind(std::plus<int>(),
      std::placeholders::_1,
      10);
   std::cout << "+10:    " << plus10(7) << std::endl;

   auto plus10times2 = std::bind(std::multiplies<int>(),
      std::bind(std::plus<int>(),
         std::placeholders::_1,
         10),
      2);
   // ...
}

我无法理解绑定对象“plus10times2”的工作原理。 它不应该绑定到 int 参数?

如何绑定另一个绑定对象?当调用 plus10times2 的调用运算符时它是如何工作的(例如 plus10times2(7))?

叫做nested binding (i.e. std::bind)。一个 std::bind 调用有一个或多个 std::binds.

在这种情况下,它的工作原理如下:

首先,调用嵌套的std::bind表达式,意思是this

std::bind(std::plus<int>(), std::placeholders::_1, 10) 

将第一个参数传递给 plus10times2,并传递给外部参数: 即

std::bind(std::multiplies<int>(), /* result of nested bind */, 2);

由于嵌套绑定表达式(即内部 std::bind(std::plus<int>() ...)returns 一个 integer,而外部绑定表达式(即 std::bind(std::multiplies<int>()...)期望一个 intger 在那个地方,它完美地工作。

更多阅读:Nested bind expressions


也就是说,从 C++11 开始,我们还有 lambda function。在 lambda 调用中将嵌套绑定表达式写入 equalent 可能会解决问题:

const auto plus10_lmda = [](int arg) { return std::plus{}(arg, 10); };
const auto plus10times2_lmda = [](auto callblePlus, int innerArg) { return std::multiplies{}(callblePlus(innerArg), 2); };
std::cout << plus10times2_lmda(plus10_lmda, 7);   // prints 34

或完全嵌套并立即调用内部 lambda

const auto plus10times2_lmda = [](int innerArg) { 
   return std::multiplies{}(
      [](int arg) { return std::plus{}(arg, 10); }(innerArg) // invoke the inner one immediately
      , 2); 
};
std::cout << plus10times2_lmda(7);  // prints 34

下面是 cppreference.com 关于 std::bind 的摘录:

"给定一个从先前调用 bind 获得的对象 g,当它在函数调用表达式 g(u1, u2, ... uM) 中调用时,会调用存储的对象,就好像通过 std::invoke(fd, std::forward(v1), std::forward(v2), ..., std::forward(vN)),其中 fd 是类型 std::decay_t 绑定参数 v1、v2、...、vN 的值和类型按以下规定确定。

...

如果存储的参数 arg 是 std::is_bind_expression::value == true 的类型 T(例如,另一个绑定表达式直接传递到对绑定的初始调用中),则绑定执行函数组合:而不是传递绑定子表达式 return 的函数对象,急切地调用子表达式,并将其 return 值传递给外部可调用对象。如果绑定子表达式有任何占位符参数,它们将与外部绑定共享(从 u1、u2 等中挑选出来)。具体来说,上面 std::invoke 调用中的参数 vn 是 arg(std::forward(uj)...) 并且同一调用中的类型 Vn 是 std::result_of_t&& (cv资格同g)."

所以,关键方面是我传递了一个参数 std::is_bind_expression<T>::value == true,它修改了“正常”std::bind 行为