admin 管理员组文章数量: 886992
现代c++急急急——functional、bind、lambda
文章目录
- 现代c++急急急——functional、bind、lambda
- bind前世今生
- 从一个排序的例子开始
- 可能的实现
- 今生
- 函数对象
- 从hello开始
- 可能的实现
- lambda
- 例子
- 从hello开始
- 带函数参数的lambda
- 捕获外界变量的lambda
现代c++急急急——functional、bind、lambda
bind前世今生
bind1st:operator()的第一个参数成为一个既定的值
bind2nd:operator()的第二个参数成为一个既定的值
从一个排序的例子开始
#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>using namespace std;template<class T>
void MyPrint(T& container)
{for (auto x : container){cout << x << ' ';}cout << endl;
}int main()
{vector<int> vec;srand(time(NULL));for (int i = 0; i < 20; i++){vec.push_back(rand() % 100 + 1);}MyPrint(vec);sort(vec.begin(), vec.end());//sort(vec.begin(), vec.end(), greater<int>());MyPrint(vec);return 0;
}
以上代码生成一个vector并从小到大排序
sort第三个参数支持自定义的排序方法,这里我们传入的是greater,实现从大到小排序,greater实现两个元素之间的比较
那么现在需要把70插入容器中,要求容器顺序不变,该怎么做?
在STL中有一个find_if
可以在遍历某个容器的时候,按某个条件查找,比如说本次要求,只需要挨个拿容器中的某个元素和70进行比较,返回bool即可
但是我们注意到,find_if
拿一个元素和一个既定的元素做比较,而greater或者less的都是拿两个元素做比较,有没有办法实现greater其中的一个元素固定
下来呢?
这就是绑定器的作用,比如说让greater的第一个元素固定为70
#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>using namespace std;template<class T>
void MyPrint(T& container)
{for (auto x : container){cout << x << ' ';}cout << endl;
}int main()
{vector<int> vec;srand(time(NULL));int num = 0;cin >> num;for (int i = 0; i < 20; i++){vec.push_back(rand() % 100 + 1);}MyPrint(vec);sort(vec.begin(), vec.end(), greater<int>());auto res = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), num));if (res != vec.end()){vec.insert(res, num);}MyPrint(vec);return 0;
}
同理,bind2nd也是一样的效果
可能的实现
find_if
// 取自cppreference
template<class InputIterator, class UnaryPredicate>InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred)
{while (first!=last) {if (pred(*first)) return first;++first;}return last;
}
bind1st
template<class Compare, class T>
class _MyBind1st
{
public:_MyBind1st(Compare cmp, T val) : _cmp(cmp), _val(val) {};bool operator()(const T& secondElement){return _cmp(_val, secondElement);}
private:Compare _cmp;T _val;
};
template<class Compare, class T>
_MyBind1st<Compare, T> myBind1st(Compare cmp, const T& val)
{return _MyBind1st<Compare, T>(cmp, val);
}
最终效果
#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>using namespace std;template<class T>
void MyPrint(T& container)
{for (auto x : container){cout << x << ' ';}cout << endl;
}template<class Compare, class T>
class _MyBind1st
{
public:_MyBind1st(Compare cmp, T val) : _cmp(cmp), _val(val) {};bool operator()(const T& secondElement){return _cmp(_val, secondElement);}
private:Compare _cmp;T _val;
};
template<class Compare, class T>
_MyBind1st<Compare, T> myBind1st(Compare cmp, const T& val)
{return _MyBind1st<Compare, T>(cmp, val);
}int main()
{vector<int> vec;srand(time(NULL));int num = 0;cin >> num;for (int i = 0; i < 20; i++){vec.push_back(rand() % 100 + 1);}MyPrint(vec);sort(vec.begin(), vec.end(), greater<int>());auto res = find_if(vec.begin(), vec.end(), myBind1st(greater<int>(), num));if (res != vec.end()){vec.insert(res, num);}MyPrint(vec);return 0;
}
运行结果正确,由此可知,绑定器是函数对象的一种用法,使得某个函数的某个参数成为既定的参数(既定的意思并不是说右值)
今生
#include <iostream>
#include <functional>
#include <string>using namespace std;void Say(string str)
{cout << str << endl;
}int main()
{auto res = bind(Say, "hello");res();return 0;
}
与1st和2nd不同的是,bind可以使得任意一个参数绑定为指定的参数,而不是固定的位置(如1st只能第一个参数变成指定的参数)
当然,bind也有1st和2nd的功能,也就是20个占位符,用于既定的位置
的参数指定成给定的参数
(很绕,我知道,这句话看一遍就好,不要深陷其中,看下面的例子)
一个实际的例子:
#include <iostream>
#include <functional>
#include <string>using namespace std;void Say(string str)
{cout << str << endl;
}int main()
{// 指明Say函数的第一个参数(指定的位置)由用户指定auto res = bind(Say, placeholders::_1);res("hello");return 0;
}
以上效果便是第一个参数被指定为由用户给出的参数
函数对象
从hello开始
#include <iostream>
#include <functional>
#include <string>using namespace std;void SayStr(string str)
{cout << str << endl;
}void Say()
{cout << "hello" << endl;
}int main()
{function<void()> funcObj = Say;function<void(string)> funcObj2 = SayStr;funcObj();funcObj2("hello");return 0;
}
可以看到,函数对象有点类似于函数指针,但是背后原理其实是模板类,重载了()
运算符,只是效果类似于函数指针,可以“指向”函数(注意有一个引号)
那么有趣的就是,函数这个概念,除了普通的函数,bind和lambda表达式都可以理解为返回了一个函数
除此之外,其实还有类内函数(比较重要的OOP手法)
#include <iostream>
#include <functional>
#include <string>using namespace std;class Say
{
public:void SayHello(string str) { cout << str << endl; };
};int main()
{function<void(Say*, string)> funcObj = &Say::SayHello;Say tmpObj;funcObj(&tmpObj, "hello world");return 0;
}
通过以上例子,可以看出要使得functional可以“指向”类内函数,需要满足以下的条件
- 类内函数为Public
- 实例化function对象的函数签名需要有一个类的指针(也就是this指针)
- 需要一个具体的类的对象
那么有趣的是,bind的参数也是一个函数,因此也可以通过传入的参数“指向”某个类的成员函数,这么做的好处是什么?
有点类似友元函数 + 回调函数的概念了,可以在不同的地方回调这个函数,极大的增加了代码的复用
可能的实现
#include <iostream>
#include <string>
#include <functional>using namespace std;void Say(string str)
{cout << str << endl;
}template<class Fty>
class myFunction {};template<class R, class Arg>
class myFunction<R(Arg)>
{
public:using pFunc = R(*)(Arg);myFunction(pFunc func) : func_(func) {};R operator() (Arg arg){return func_(arg);};
private:pFunc func_;
};int main()
{myFunction<void(string)> func(Say);func("hello world");return 0;
}
这里可以看出其实就是使用了函数指针来实现的,唯一的差别就是,这个实现只是一个参数,官方实现应该是可变参
// c++ insights的结果更容易看出实现
#include <iostream>
#include <string>
#include <functional>using namespace std;void Say(std::basic_string<char> str)
{std::operator<<(std::cout, str).operator<<(std::endl);
}template<class Fty>
class myFunction
{
};/* First instantiated from: insights.cpp:31 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class myFunction<void (std::basic_string<char>)>
{public: using pFunc = void (*)(std::basic_string<char>);inline myFunction(pFunc func): func_{func}{}inline void operator()(std::basic_string<char> arg){return this->func_(std::basic_string<char>(arg));}private: pFunc func_;public:
};#endiftemplate<class R, class Arg>
class myFunction<R (Arg)>
{public: using pFunc = R (*)(Arg);inline myFunction(pFunc func): func_{func}{}inline R operator()(Arg arg){return this->func_(arg);}private: pFunc func_;
};int main()
{myFunction<void (std::basic_string<char>)> func = myFunction<void (std::basic_string<char>)>(Say);func.operator()(std::basic_string<char>("hello world", std::allocator<char>()));return 0;
}
lambda
语法格式:
[]<class T>()->{};
[]:捕获列表
<class T>
:c++20新增的,也就是lambda表达式模板
():函数参数列表
->:函数返回类型,c++11的语法,后置返回类型,也有叫尾置返回
{}:函数体
注意,有一个分号
再注意,lambda表达式模板是c++20的语法,此前的c++标准是没有的
lambda表达式的实现原理其实就是模板类,重载了()
运算符
例子
从hello开始
#include <iostream>
#include <functional>
#include <string>using namespace std;void Say(string str)
{cout << str << endl;
}int main()
{auto res = []()->void { cout << "hello" << endl; };res();return 0;
}
// c++ inights的结果
#include <iostream>
#include <functional>
#include <string>using namespace std;void Say(std::basic_string<char> str)
{std::operator<<(std::cout, str).operator<<(std::endl);
}int main()
{class __lambda_14_13{public: inline /*constexpr */ void operator()() const{std::operator<<(std::cout, "hello").operator<<(std::endl);}using retType_14_13 = auto (*)() -> void;inline constexpr operator retType_14_13 () const noexcept{return __invoke;};private: static inline /*constexpr */ void __invoke(){__lambda_14_13{}.operator()();}};__lambda_14_13 res = __lambda_14_13{};res.operator()();return 0;
}
可以看到,lambda本质就是一个模板类 + 重载()
运算符
带函数参数的lambda
#include <iostream>
#include <functional>
#include <string>using namespace std;void Say(string str)
{cout << str << endl;
}int main()
{auto res = [](int a, int b)->int { return a + b; };cout << res(1, 2) << endl;return 0;
}
捕获外界变量的lambda
#include <iostream>
#include <functional>
#include <string>using namespace std;void Say(string str)
{cout << str << endl;
}int val;int main()
{int a = 0, b = 0;cin >> a >> b;// 值捕获auto res = [a, b]()->int { return a + b; };cout << res() << endl;// 引用捕获auto ans = [&a, &b]()->int { a++, b++; return a + b; };cout << ans() << endl;// 值捕获,但是捕获当前作用域所有变量:局部和全局auto cnt = [=]()->int { return val + b; };cout << cnt() << endl;// 引用捕获,但是捕获当前作用域所有变量:局部和全局auto tmp = [&]()->int { return val + b; };cout << tmp() << endl;return 0;
}
关于捕获列表,还有其他花活,比如说捕获this指针,值捕获,但是某些变量引用捕获以及一个比较重要的:值捕获,但是可以修改捕获的变量(mutable),有兴趣可以自行了解
至于尾置返回类型,是可以省略的,如果省略了,就默认返回void
本文标签: 现代c急急急functionalbindlambda
版权声明:本文标题:现代c++急急急——functional、bind、lambda 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1732356616h1534612.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论