std::function
std::function API文档
存在头文件<functional>
定义
类模板 std::function
是通用多态函数封装器。 std::function
的实例能存储、复制及调用任何可调用 (Callable) 目标——函数、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。
存储的可调用对象被称为 std::function
的目标。若 std::function
不含目标,则称它为空。调用空 std::function
的目标导致抛出 std::bad_function_call
异常。
std::function
满足可复制构造 (CopyConstructible) 和可复制赋值 (CopyAssignable) 。
测试代码
#include <functional>
#include <iostream>
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
void print_num(int i)
{
std::cout << i << '\n';
}
struct PrintNum {
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// 存储自由函数
std::function<void(int)> f_display = print_num;
f_display(-9);
// 存储 lambda
std::function<void()> f_display_42 = []() { print_num(42); };
f_display_42();
// 存储到 std::bind 调用的结果
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337();
// 存储到成员函数的调用
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
const Foo foo(314159);
f_add_display(foo, 1);
f_add_display(314159, 1);
// 存储到数据成员访问器的调用
std::function<int(Foo const&)> f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';
// 存储到成员函数及对象的调用
using std::placeholders::_1; //std::placeholders::_1 是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代。
std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
f_add_display2(2);
// 存储到成员函数和对象指针的调用
std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
f_add_display3(3);
// 存储到函数对象的调用
std::function<void(int)> f_display_obj = PrintNum();
f_display_obj(18);
}
std::forward
std::forward API 文档
位于头文件<utility>
定义
std::forward
通常是用于完美转发的,它会将输入的参数原封不动地传递到下一个函数中,这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值。
(当我们将一个右值引用传入函数时,他在实参中有了命名,所以继续往下传或者调用其他函数时,根据C++ 标准的定义,这个参数变成了一个左值。那么他永远不会调用接下来函数的右值版本,这可能在一些情况下造成拷贝。为了解决这个问题 C++ 11引入了完美转发,根据右值判断的推倒,调用std::forward
传出的值,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。)
Lambda 表达式
lambda表达式 API文档
能够捕获作用域中的变量的无名函数对象。
lambda的基础语法定义如下:
[capture](parameters) mutable ->return-type{statement}
其中,
1、[capture]:捕获列表。它总是出现在lambda函数的开始位置。在编译器看来[]是lambda的引出符号,编译器正式通过它来判断接下来的代码是否是lambda函数。捕获列表能够捕捉当前上下文中的变量供给lambda函数使用。具体的capture列表中的语法,下面还会详细讲述。
2、(parameters):参数列表。它跟一般函数的参数列表一样,使用规则也相同。在lambda中,如果不需要传入参数,可以省略。
3、mutable:修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消它的常量属性。显示指定mutable修饰符的时候,参数列表不能省略。
4、->return-type:返回值类型。->这个同C++11新引入的追踪返回值类型的声明是一致的,语法也是一致的。不同的是,处于方便,lambda函数在没有返回值的情况下,可以省略掉(在某些编译器可以推导出返回值类型的情况亦可省略)。
5、{statement}:函数体。与一般函数的函数体一致,额外可以使用捕获列表中捕获的变量。
上面就是lambda函数的语法,可以看出2和3、4都是可选的。
[capture] 捕获方式
1、[a]表示值传递捕获变量a(多个参数可以用逗号分隔)
2、[=]表示值传递捕获上下文所有变量
3、[&a]表示引用传递捕获变量a
4、[&]表示引用传递捕获上下文所有变量
5、[this]表示值传递捕获当前的this指针
6、[=, &a, &b]表示值传递捕获上下文所有变量,但是a、b变量以引用传递方式捕获。
7、[&, a, this]表示引用传递捕获上下文所有变量,但是a和this指针以值传递方式捕获
结合起来实现不定参数的回调函数
定义一个函数。Fn接收函数体,…Params接收不定参数
#include <iostream>
#include <funtional>
//不定参数的回调函数的模板
template<typename Fn, typename ...Params>
void functionTest(Fn&& fn, Params && ...params)
{
std::function<void()> fn1 = std::bind(std::forward<Fn>(fn),std::forward<Params>(params)...);
fn1();
}
//测试
//普通函数
void print_num1(int i)
{
std::cout << i << std::endl;
}
//仿函数
struct print_num2
{
void operator()(int i){
std::cout<<i<<std::endl;
}
};
int main(){
functionTest(print_num1,1);
functionTest(print_num2(),2);
// lambda表达式
functionTest(
[](int i)->void
{
std::cout<<i<<std::endl
},
3);
}