C++11 bind function forward 用法详解

目录

std::bind

std::function

std::forward


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 的使用,而没有真正修改参数的值。

猜你喜欢

转载自blog.csdn.net/weixin_40582034/article/details/132040437