zoukankan      html  css  js  c++  java
  • C++ Primer学习笔记 参数绑定bind

    问题的引出

    使用find_if时,可以用lambda表达式作为其第三个可调用对象的实参。然而,lambda表达式只适合用于简单的场景,如表达式只用一到两次。当可调用单元要要大量复用时,最好还是能写成函数形式,于是,我们写出check_size函数,用于过滤出尺寸大于sz的字符串。

    bool check_size(const string& s, string::size_type sz)
    {
    	return s.size() >= sz;
    }
    
    int main()
    {
    	using std::placeholders::_1;
    
    	vector<string> vec = { "hello", "y", "no", "1234567" };
    	string::size_type sz = 2;
    	
    	auto it = find_if(vec.begin(), vec.end(), [sz](const string& s) -> bool {
    		return s.size() >= sz;
    	});
    	cout << *it << endl;
    	return 0;
    }
    

    问题在于,find_if的第三个参数(可调用对象)只会给形参传入1个实参,而我们定义的check_size需要2个参数。该怎么办?
    可以使用bind库函数解决该问题。

    标准库bind函数

    bind库函数可以看做是定义了一个新函数,能在原来函数基础上,只需要更少的传入参数,绑定原来函数所需要的参数,修改参数(列表中)的顺序。
    头文件

    其一般调用形式:

    auto newCallable = bind(callable, arg_list);
    /**
    newCallable 是一个可调用对象
    arg_list 是参数分隔的参数列表,对应给定的callable参数
    */
    

    当我们调用newCallable 时,newCallable 会调用callable,并传入arg_list作为callable参数。

    arg_list可能包含形如_n(n=1,2,3,...)的名字,作为“占位符”,表示newCallable的参数,占据了传递给newCallable 的参数的“位置”。比如,_1表示newCallable的第一个参数位置,_2表示newCallable 的第2个参数位置。
    注意:
    1)bind中的_1, _2等没有顺序要求;
    2)_1, _2等占位符,代表的是newCallable对应位置中的参数,而非callable的;
    3)_1, _2等决定了newCallable的形参个数,实参由newCallable的调用者决定。bind内除了第一个参数callable,其他参数都作为callable的实参,callable形参由其定义决定;

    如何绑定callable的参数?

    如上面例子中,如何绑定check_size的sz参数?

    #include <functional>
    
    int sz = 6;
    auto checksz = bind(check_size, _1, sz); 
    string s = "hello"
    bool b1 = checksz(s); // 调用checksz(s)等价于调用check_size(s, 6) 
    
    auto it2 = find_if(vec.begin(), vec.end(), checksz); // find_if会传入1个参数给checksz的_1参数,因此需要注意_1和sz的顺序不能颠倒
    cout << *it2 << endl;
    

    使用placeholders名字

    前面的程序无法通过编译,无法识别像"_1"这样的标识符,因为它位于名为placeholders的命名空间,需要在使用前用using声明。

    如,"_1"的using声明

    using std::placeholders::_1;
    ...
    

    每个占位符单独声明,很繁琐,我们可以用using指示,一次引入所有占位符:

    using namespace std::placeholders;
    ...
    

    bind的参数

    可以用bind为可调用对象绑定参数,重新安排顺序,
    如,f是一个可调用对象,有5个参数,g是生成的新可调用对象,有2个参数:

    auto g = bind(f, a, b, _2, c, _1); // 调用g(_1, _2)会调用f(a, b, _2, c, _1)
    

    用bind重排参数顺序

    利用新可调用对象到原来可调用对象的映射,可以重新排列参数顺序。
    比如,上面例子中,

    auto g = bind(f, a, b, _2, c, _1); // 调用g(_1, _2)会调用f(a, b, _2, c, _1)
    
    auto g2 = bind(f, a, b, _1, c, _2); // 调用g(_1, _2)会调用f(a, b, _1, c, _2)
    

    再比如,

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

    当sort对2个元素A和B比较时,会调用isShorter(A, B)。在bind重排参数顺序后,会相当于调用isShorter(B, A)。

    ref()绑定引用参数

    默认情况下,bind中普通参数(非占位符(_1,_2,...))是以值拷贝方式传递给bind返回的新可调用对象中(比如上面例中的a,b,c),而非引用方式传值。然而,有时我们希望以引用方式传递参数,比如参数对象禁止拷贝,或者拷贝代价较高,该如何进行?
    类似于lambda表达式中的值拷贝、引用拷贝。一样可以用标准库ref函数传递给bind对象引用作为参数。

    ostream& print(ostream &os, const string& s, char c) 
    {
    	return os << s << c;
    }
    
    // 错误:不能拷贝os
    for_each(words.begin(), words.end(), bind(print, os, _1, ' ')); // 调用newprint(v)会调用print(os, v, ' '), 值传递os会导致拷贝
    
    // OK:引用传递os
    for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' ')); // 调用newprint(v)会调用print(ref(os), v, ' '), 值传递os会导致引用拷贝,而os引用是允许拷贝的
    

    另外,对于const对象,可以用cref函数来传递引用。ref和cref都位于

    小结

    1)当需要的可调用对象和现有函数由于参数数量、顺序不兼容,而又不想重新定义一个函数时,可以考虑使用bind库函数为现有函数绑定参数、调整参数顺序;
    2)原函数需要引用传递参数时,可使用ref、cref库函数为bind传递对象引用;

  • 相关阅读:
    回发保留前台添加的html
    关于NBear数据访问层IDData
    使用js把数字转化成会计格式
    二次注入
    .htaccess利用与Bypass方式总结
    HTTPoxy漏洞(CVE-2016-5385)
    JAVA并行程序基础一
    队列-数组实现
    Vuex
    稀疏数组
  • 原文地址:https://www.cnblogs.com/fortunely/p/15636184.html
Copyright © 2011-2022 走看看