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传递对象引用;

  • 相关阅读:
    XAF 有条件的对象访问权限
    XAF 顯示 UnInplace Report(設置自定義條件顯示報表,不是根據選擇ListView記錄條件顯示報表)
    XAF 如何自定义PivotGrid单元格显示文本?
    XAF 如何布局详细视图上的按钮
    XAF How to set size of a popup detail view
    XAF Delta Replication Module for Devexpress eXpressApp Framework
    XAF 帮助文档翻译 EasyTest Basics(基础)
    XAF 用户双击ListView记录时禁止显示DetailView
    XAF How to enable LayoutView mode in the GridControl in List Views
    XAF 如何实现ListView单元格批量更改?
  • 原文地址:https://www.cnblogs.com/fortunely/p/15636184.html
Copyright © 2011-2022 走看看