标准库bind函数

发布于:2024-05-10 ⋅ 阅读:(23) ⋅ 点赞:(0)

标准库bind函数

我们可以解决向check_size 传递一个长度参数的问题,方法是使用一个新的名为bind的标准库函数,它定义在头文件functional中。

可以将bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式为:

auto newCallable = bind(callable, arg_list);

其中,newCallable本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的callable的参数。即,当我们调用newCallable时,newCallable 会调用callable,并传递给它arg_list中的参数。

arg_list中的参数可能包含形如n的名字,其中n是一个整数。这些参数是“占位符”,表示 newCallable的参数,它们占据了传递给 newCallable的参数的“位置”。

数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,依此类推。

绑定check_size的sz参数

作为一个简单的例子,我们将使用bind生成一个调用check_size的对象,如下所示,它用一个定值作为其大小参数来调用check_size:

// check6是一个可调用对象,接受一个string类型的参数
//并用此string和值6来调用check_size
auto check6 = bind(check_size,_1,6);

此bind调用只有一个占位符,表示check6只接受单一参数。

占位符出现在arg_list的第一个位置,表示 check6的此参数对应 check_size的第一个参数。此参数是一个const string&。

因此,调用check6必须传递给它一个string类型的参数,check6会将此参数传递给check_size。

string s = "hello";
bool bl = check6(s);// check6(s)会调用 check size(s,6)

使用bind,我们可以将原来基于lambda的find_if调用:

auto wc=find_if(words.begin(),words.end(),
                    [szl(const string &a)


替换为如下使用check_size的版本:

auto wc = find_if(words.begin(),words.end(),
                        bind(check _size,_1, sz));

此bind调用生成一个可调用对象,将check_size的第二个参数绑定到 sz 的值。

当find_if对words中的string调用这个对象时,这些对象会调用check_size,将给定的string 和sz传递给它。因此,find_if可以有效地对输入序列中每个string调用check_size,实现string的大小与sz的比较。

使用placeholders名字

名字_n都定义在一个名为placeholders的命名空间中,而这个命名空间本身定义在std命名空间中。为了使用这些名字,两个命名空间都要写

与我们的其他例子类似,对bind的调用代码假定之前已经恰当地使用了using声明

例如,_1对应的using声明为:

using std::placeholders::_1;

此声明说明我们要使用的名字_1定义在命名空间placeholders中,而此命名空间又定义在命名空间std中。

对每个占位符名字,我们都必须提供一个单独的using 声明。编写这样的声明很烦也很容易出错。可以使用另外一种不同形式的using 语句,而不是分别声明每个占位符,如下所示:

using namespace namespace _name;

这种形式说明希望所有来自 namespace _name的名字都可以在我们的程序中直接使用。例
如:

using namespace std::placeholders;

使得由placeholders定义的所有名字都可用。与bind函数一样,placeholders命名空间也定义在functional头文件中。

bind的参数

如前文所述,我们可以用bind修正参数的值。更一般的,可以用bind绑定给定可调用对象中的参数或重新安排其顺序。例如,假定是一个可调用对象,它有5个参数,则下面对bind的调用:

//g是一个有两个参数的可调用对象
auto g= bind(f, a, b, _2, C,_1);

生成一个新的可调用对象,它有两个参数,分别用占位符_2和_1表示。

这个新的可调用对象将它自己的参数作为第三个和第五个参数传递给f。f的第一个、第二个和第四个参数分别被绑定到给定的值a、b和c上。

传递给g的参数按位置绑定到占位符。即,第一个参数绑定到_1,第二个参数绑定到2。因此,当我们调用g时,其第一个参数将被传递给f作为最后一个参数,第二个参数将被传递给f作为第三个参数。实际上,这个bind调用会将

g(_1,_2);

映射为

f(a, b,2, c,_1);

即,对g的调用会调用f,用g的参数代替占位符,再加上绑定的参数a、b和c。

例如,

调用

g(X,Y);

会调用

f(a, b, Y, c, X);

用bind重排参数顺序

下面是用bind重排参数顺序的一个具体例子,我们可以用bind颠倒isShroter的含义:

// 按单词长度由短至长排序
sort(words.begin(),words.end(),isShorter);

// 按单词长度由长至短排序
sort(words.begin(), words.end(), bind(isShorter,_2,_1));

在第一个调用中,当sort需要比较两个元素A和B时,它会调用isShorter(A,B)。

在第二个对sort的调用中,传递给isShorter的参数被交换过来了。

因此,当sort比较两个元素时,就好像调用isShorter(B,A)一样。

绑定引用参数

默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中。

但是,与lambda 类似,有时对有些绑定的参数我们希望以引用方式传递,或是要绑定参数的类型无法拷贝。
例如,为了替换一个引用方式捕获ostream 的 lambda:

//os是一个局部变量,引用一个输出流
// c是一个局部变量,类型为char
for each(words.begin(), words.end(),
            [&os, c](const string &s) { os << s<< c; });

可以很容易地编写一个函数,完成相同的工作:

ostream &print(ostream &os, const string &s, char c)
{
return os << s << c;
}

但是,不能直接用bind来代替对os的捕获:

//错误:不能拷贝0s
for_each(words.begin(), words.end(), bind(print, os,_1,' '));

原因在于bind拷贝其参数,而我们不能拷贝一个ostream。如果我们希望传递给bind一个对象而又不拷贝它,就必须使用标准库ref函数:

for_each(words.begin(), words.end(),
            bind(print, ref(os),_1,''));


函数ref返回一个对象,包含给定的引用,此对象是可以拷贝的。标准库中还有一个 cref函数,生成一个保存const 引用的类。与bind一样,函数ref和cref也定义在头文件 functional中。

向后兼容:参数绑定
旧版本 C++提供的绑定函数参数的语言特性限制更多,也更复杂。

标准库定义了两个分别名为bind1st和bind2nd的函数。

类似bind,这两个函数接受一个函数作为参数,生成一个新的可调用对象,该对象调用给定函数,并将绑定的参数传递给它。

但是,这些函数分别只能绑定第一个或第二个参数。由于这些函数局限太强,在新标准中已被弃用(deprecated)。所谓被弃用的特性就是在新版本中不再支持的特性。

新的C++程序应该使用bind。


网站公告

今日签到

点亮在社区的每一天
去签到