目录
std::bind
std::bind
是一个函数模板,定义在 <functional>
头文件中。它的作用是将一个可调用对象(比如函数、函数指针、成员函数、成员函数指针等等)以及若干个参数绑定到一个新的函数对象上,形成一个新的可调用对象。
std::bind
的函数签名如下:
template< class F, class... Args >
constexpr /*unspecified*/ bind( F&& f, Args&&... args );
其中,F
表示待绑定的可调用对象类型,Args
表示待绑定的参数类型。std::bind
返回一个新的函数对象,这个函数对象可以被调用,调用时会执行绑定后的可调用对象,并将绑定的参数传递给它。
下面是一个示例,演示了如何使用 std::bind
函数将一个普通函数绑定到一个新的函数对象上:
#include <iostream>
#include <functional>
int add(int a, int b) {
return a + b;
}
int main() {
auto add_five = std::bind(add, 5, std::placeholders::_1);
std::cout << add_five(3) << std::endl; // 输出 8
return 0;
}
在这个示例中,我们使用 std::bind
函数将 add
函数绑定到一个新的函数对象 add_five
上,并将 5 绑定到 add
函数的第一个参数上,将 _1
占位符绑定到 add
函数的第二个参数上。然后,在调用 add_five(3)
时,我们传递了 3 作为 _1
占位符的值,add_five
函数对象会调用 add(5, 3)
函数并返回结果 8。
在使用 std::bind
函数时,可以使用占位符来指定哪些参数需要在调用函数对象时传递,哪些参数需要在绑定时传递。常用的占位符有:
_1
:表示调用函数对象时的第一个参数。_2
:表示调用函数对象时的第二个参数。_3
:表示调用函数对象时的第三个参数。- ...
例如,如果我们希望在调用函数对象时传递第一个参数和第三个参数,而将第二个参数在绑定时传递,可以这样使用占位符:
auto foo = std::bind(func, std::placeholders::_1, arg2, std::placeholders::_3);
在绑定参数时,也可以使用 std::ref
函数将一个变量包装成一个引用,以便在调用函数对象时使用引用传递。例如:
int x = 42;
auto foo = std::bind(func, std::ref(x), std::placeholders::_1);
这里,我们将 x
变量包装成一个引用,并将它与占位符一起绑定到 func
函数上。在调用 foo
函数对象时,第一个参数将会是 x
的引用。
除了 std::bind
函数外,C++11 还提供了一个新的语言特性——Lambda 表达式,可以用来替代 std::bind
函数。Lambda 表达式可以更加灵活地实现函数对象的绑定和参数传递。例如:
auto add_five = [](int x) { return add(5, x); };
std::cout << add_five(3) << std::endl; // 输出 8
在这个示例中,我们使用 Lambda 表达式将 add(5, x)
函数绑定到一个新的函数对象 add_five
上,并将 3 作为参数传递给它。add_five
函数对象会调用 add(5, 3)
函数并返回结果 8。
std::function
C++11 中引入了 std::function
类型,它是一个多态函数封装类,可以用来存储和调用任意可调用对象,包括函数指针、函数对象、Lambda 表达式等等。
std::function
的函数签名如下:
template <typename Signature>
class function;
其中 Signature
表示可调用对象的签名,包括返回值类型和参数列表。例如,一个返回 int
类型,接受两个 int
类型参数的函数的签名为 int(int, int)
。
std::function
类型可以通过构造函数或赋值操作符来绑定一个可调用对象,并可以在需要调用该对象的时候,使用 operator()
运算符来执行它。
以下是一个示例,演示了如何使用 std::function
类型存储和调用一个 Lambda 表达式:
#include <iostream>
#include <functional>
int main() {
std::function<int(int, int)> add = [](int x, int y) { return x + y; };
std::cout << add(3, 4) << std::endl; // 输出 7
return 0;
}
在这个示例中,我们定义了一个 std::function<int(int, int)>
类型的对象 add
,并使用一个 Lambda 表达式将其绑定到一个返回两个参数之和的函数上。然后,在调用 add(3, 4)
时,我们传递了 3 和 4 作为参数,并执行了 [](int x, int y) { return x + y; }
Lambda 表达式,返回结果 7。
std::function
类型还支持空函数对象和函数对象的比较,可以通过 operator bool()
运算符来判断对象是否为空,以及通过 operator==
和 operator!=
运算符来比较两个函数对象是否相等。
以下是一个示例,演示了如何使用 std::function
类型进行函数对象的比较:
#include <iostream>
#include <functional>
int main() {
std::function<void()> func1 = []() { std::cout << "Hello, world!" << std::endl; };
std::function<void()> func2;
std::cout << std::boolalpha << (func1 == func2) << std::endl; // 输出 false
func2 = func1;
std::cout << std::boolalpha << (func1 == func2) << std::endl; // 输出 true
return 0;
}
在这个示例中,我们定义了两个 std::function<void()>
类型的对象 func1
和 func2
,并将 func1
绑定到一个 Lambda 表达式上。然后,在比较 func1
和 func2
是否相等时,我们使用了 operator==
运算符,并输出了比较结果。由于此时 func2
还未被绑定到任何可调用对象上,因此 func1
和 func2
不相等,输出 false。接着,我们将 func2
绑定到和 func1
相同的可调用对象上,再次比较它们是否相等,并输出了比较结果。此时,func1
和 func2
相等,输出 true。
std::forward
std::forward
是 C++11 标准库中的一个函数模板,用于完美转发参数。它的主要作用是在函数模板中将传入的参数按照它们的值类别(左值或右值)进行转发,以保持它们的值类别不变,从而避免不必要的拷贝和移动操作。
std::forward
的函数签名如下:
template <typename T>
constexpr T&& forward(typename std::remove_reference<T>::type& arg) noexcept;
template <typename T>
constexpr T&& forward(typename std::remove_reference<T>::type&& arg) noexcept;
其中,第一个模板参数 T
表示要转发的参数的类型。如果参数是左值引用类型,那么 T
就是这个引用类型的基本类型。如果参数是右值引用类型,那么 T
就是这个右值引用类型的基本类型加上 &&
。
std::forward
的作用是将传入的参数 arg
转发给另一个函数(通常是模板函数),并保持它原本的值类别。在转发的过程中,如果参数是左值引用类型,那么转发后的类型也是左值引用类型。如果参数是右值引用类型,那么转发后的类型也是右值引用类型。
以下是一个示例,演示了如何使用 std::forward
进行完美转发:
cpp
Copy
#include <utility>
#include <iostream>
void foo(int& x) {
std::cout << "lvalue" << std::endl;
++x;
}
void foo(int&& x) {
std::cout << "rvalue" << std::endl;
++x;
}
template <typename T>
void bar(T&& x) {
std::forward<T>(x);
foo(std::forward<T>(x));
}
int main() {
int i = 42;
bar(i); // 输出 "lvalue"
std::cout << i; // 输出 43
bar(42); // 输出 "rvalue"
return 0;
}
在这个示例中,我们定义了两个函数 foo
,分别接受一个左值引用类型和一个右值引用类型的参数。然后,我们定义了一个模板函数 bar
,它接受一个参数 x
,类型为 T&&
。在 bar
函数中,我们使用 std::forward
将 x
完美转发给 foo
函数,并保持它的值类别不变。当 bar
函数被调用时,如果传入的参数是一个左值,那么 bar
函数将会调用 foo(int&)
函数,并输出 "lvalue"。在 foo(int&)
函数中,我们将参数加一,然后将结果存回原参数。因此,当我们输出 i
的值时,它的值变为 43。如果传入的参数是一个右值,那么 bar
函数将会调用 foo(int&&)
函数,并输出 "rvalue"。在 foo(int&&)
函数中,我们将参数加一,但由于它是一个右值,所以无法修改它的值。因此,在这个示例中,我们只是演示了 std::forward
的使用,而没有真正修改参数的值。