std::bind

本文详细介绍了C++中std::bind函数的使用方法,包括如何使用std::placeholders,如何处理引用参数,以及std::bind与绑定函数之间的中间层。通过多个示例,展示了std::bind在不同场景下的应用,如绑定函数、成员函数和数据成员。

std::bind返回一个function object,可以绑定:

  1. functions;
  2. function objects;
  3. member functions, _1必须是某个object地址;
  4. data members, _1必须是某个object.

std::bind()时参数或std::placeholders出现的顺序、原函数参数的顺序、新函数参数的顺序,是怎么对应的?

  • std::bind()时参数或std::placeholders出现的顺序与原函数参数是一一对应的;
  • 新函数参数与std::bind()std::placeholders是一一对应的,即新函数的第一个参数对应于_1,新函数的第二个参数对应于_2,依此类推。

1. bind的一般使用方式

#include <functional>
#include <string>
#include <iostream>

void check(const std::string& name, int age) {
    std::cout << name << " " << age << std::endl;
}

int main()
{
    /* placeholders表示的是newcallable的参数 */

    // newcallable1的第一个参数放到_1处,第二个参数放到_2处
    auto newcallable1 = std::bind(check, std::placeholders::_1, std::placeholders::_2);
    newcallable1("yujia", 1);

    // newcallable2的第一个参数放到_1处,第二个参数放到_2处,相当于newcallable1的参数互换
    auto newcallable2 = std::bind(check, std::placeholders::_2, std::placeholders::_1);
    newcallable2(1, "yujia");

    auto newcallable3 = std::bind(check, std::placeholders::_1, 1);
    newcallable3("yujia");

    // newcallable4的第二个参数放到_2处
    // 因此即使只有一个placeholder当调用newcallable4的时候还是要提供两个参数
    // 但是调用newcallable4时提供的参数是没用的
    auto newcallable4 = std::bind(check, std::placeholders::_2, 25);
    newcallable4(123,"yujia");  // 输出:yujia 25, 而不是:yujia 123

    return 0;
}

输出:
在这里插入图片描述

1.1 bind的函数参数有引用

#include <functional>
#include <fstream>
#include <string>
#include <iostream>

std::ostream& Print(std::ostream & of, std::string& str, int i) {
    return of << str << "  " << i << std::endl;
}

using namespace std::placeholders;

int main()
{
    std::ofstream out_file("ref.txt");
    std::string str = std::string("content sample");

    // 如果out_file不用placeholder方式则需要使用std::ref,因为其不可复制
#if 0
    auto newCallable = std::bind(Print, std::ref(out_file), _1, _2);
    newCallable(str, 65);
#endif
#if 1  // 使用placeholder则没有必要使用std::ref(当然也可以使用)
    auto newCallable = std::bind(Print, _3, _1, _2);
    newCallable(str, 45, out_file);
#endif

    return 0;
}

2. std::bind和其绑定的函数之间有个中间层

#include <iostream>
#include <functional>

namespace test_ref
{
    void func(int& a, int& b, const int& c)
    {
        ++a;
        ++b;
    }
    
    auto main() -> int
    {
        std::cout << "testing ref......\n" << std::endl;

        {    
            int a = 1, b = 1, c = 1;
            func(a,b,c);
            std::cout << a << ", " << b << ", " << c << std::endl;  // 2 2 1
        }
        {
            int a = 1, b = 1, c = 1; 
            // 如果不加std::ref,则给bind传递的是copy of a,然后再将a的拷贝传给func
            auto bfunc = std::bind(func, a, std::ref(b), std::cref(c));
            bfunc();
            std::cout << a << ", " << b << ", " << c << std::endl; // 1 2 1
        }
		std::cout << "------------------------------" << std::endl;

        return 0;
    }
}

3. std::bind<int/double/class/struct>?

#pragma once

#include <iostream>
#include <functional>
#include <string>

namespace test_bind
{
    struct Foo{
        int data_;
        double divide(double n1, double n2){
            if(n2){return n1 / n2;}
            else return 0;
        }
    };

    using namespace std::placeholders;
    auto main()-> void {
        std::cout << "test_bind......." << std::endl;
       
        // 使用bind用函数代替数据成员,使用placeholder
        auto bind2mem1 = std::bind(&Foo::data_, _1);
        Foo f{20};
        std::cout << bind2mem1(f) << std::endl;
        // 使用bind用函数代替数据成员,直接绑定类对象
        auto bind2mem2 = std::bind(&Foo::data_, &f);
        std::cout << bind2mem2() << std::endl;

        std::cout << std::string(20,'-') << std::endl;

        // 使用bind绑定到函数成员
        auto bind2memfunc1 = std::bind(&Foo::divide, _2, _1, _3);
        std::cout << bind2memfunc1(15, &f, 2) << std::endl;
        auto bind2memfunc2 = std::bind(&Foo::divide, &f, _1, _2);
        std::cout << bind2memfunc2(17,2) << std::endl;

        auto bind2memfunc = std::bind<int>(&Foo::divide, &f, 19, 2);
        std::cout << bind2memfunc() << std::endl;

        std::cout << "test_bind pass\n----------------------------" << std::endl;
    }
}                                                                                                                                                                                                           

输出:

test_bind.......
20
20
--------------------
7.5
8.5
9
test_bind pass
----------------------------
&lt;think&gt;首先,用户的问题是关于C++中的std::bind,询问其使用方法、原理和示例。我需要参考提供的引用内容,因为它们是与主题相关的。 回顾引用内容: - 引用[1]: 描述了std::bind的基本用法,是C++11引入的,用于绑定函数及其参数,生成新的可调用对象,返回std::function对象。 - 引用[2]: 提到std::function和std::bind一起使用,std::function可以存储可调用对象,std::bind用于绑定函数和参数。 - 引用[3]: 解释了std::bind位于&lt;functional&gt;头文件中,用于绑定函数参数或成员函数与对象,生成新的可调用对象。 - 引用[4]: 给出了一个示例:绑定函数f,使用占位符_1,调用时只需一个参数。 用户上一次的问题是我询问std::bind的相关信息,包括使用方法、原理、示例等。所以,我需要结构化回答这些方面。 系统级指令: - 所有行内数学表达式使用$...$格式。但在这个上下文中,是关于C++代码,可能没有数学表达式。如果需要,我会处理。 - 独立公式使用$$...$$并单独成段。同样,可能不适用。 - LaTeX语法正确:确保代码块正确格式。 - 使用中文回答。 - 生成相关问题:在回答结束时,生成相关问题。 - 引用段落末尾添加引用标识:例如[^1]。 回答结构: 1. **介绍std::bind**:简要说明它是什么。 2. **使用方法**:解释如何使用,包括基本语法、绑定普通函数、绑定成员函数、使用占位符等。 3. **原理**:解释其背后的原理,如生成函数对象。 4. **示例**:提供代码示例,演示不同情况的绑定。 5. **总结**:简要总结。 最后,生成相关问题。 确保回答基于引用内容。引用来自站内,所以我需要在相关内容末尾添加引用标识。 例如,如果我说&ldquo;std::bind返回一个std::function对象[^1]&rdquo;,则在句末加[^1]。 现在,梳理内容: - 用法:从引用[1]和[4],bind接受函数和参数,可以使用占位符。格式:std::bind(function, arg1, arg2, ..., argN)。占位符如_1, _2等表示参数位置。 - 原理:从引用[3],它生成一个新的可调用对象,存储绑定的函数和参数。 - 示例:从引用[4],auto ff = std::bind(f, _1, &#39;c&#39;, 2.2); 然后调用ff(10)等同于f(10, &#39;c&#39;, 2.2)。 我还应该包括绑定成员函数的情况,因为引用[3]提到可以将成员函数和对象绑定。 代码示例需要使用代码块,如示例中所示。 使用中文回答。 生成相关问题:类似示例,如&ldquo;std::bind的性能如何?&rdquo;、&ldquo;std::bind与lambda表达式的区别?&rdquo;等。 结构化回答: 1. **引言**:介绍std::bind是什么。 2. **使用方法**:详细步骤。 3. **原理**:解释工作机制。 4. **示例**:提供多个示例代码。 5. **结论**:总结优点。 6. **相关问题**:列出相关问题。 在相关内容添加引用。 &lt;/think&gt;### C++ std::bind 使用方法、原理及示例 在 C++ 编程中,`std::bind` 是 C++11 引入的一个强大工具,位于 `&lt;functional&gt;` 头文件中。它允许你将函数(或成员函数、函数对象)与其参数绑定,生成一个新的可调用对象(通常是一个 `std::function` 对象)。这在需要延迟函数调用、适配函数接口或存储函数状态时非常有用[^1]。下面我将从使用方法、原理和示例三个方面详细解释。 #### 1. 使用方法 `std::bind` 的基本语法如下: ```cpp auto bound_function = std::bind(target_function, arg1, arg2, ..., argN); ``` - **target_function**:要绑定的原始函数,可以是普通函数、成员函数、lambda 表达式或函数对象。 - **arg1, arg2, ..., argN**:参数列表,可以是具体值或占位符(如 `std::placeholders::_1`, `_2` 等)。占位符表示参数位置,在调用 `bound_function` 时需要提供实际值。 - **返回值**:生成一个新的可调用对象(类型为 `std::function`),可以通过 `operator()` 调用。 关键用法点: - **绑定普通函数**:直接传入函数名和参数。 - **绑定成员函数**:需要绑定对象的指针或引用(使用 `&amp;ClassName::method` 和对象实例),因为成员函数隐含 `this` 指针[^3]。 - **使用占位符**:占位符(如 `_1`, `_2`)允许部分参数在绑定后指定,提高灵活性,例如绑定多个参数时只留一个占位符[^4]。 - **与 `std::function` 搭配**:`std::bind` 的结果可以存储到 `std::function` 对象中,用于统一处理不同可调用对象[^2]。 使用前需包含头文件:`#include &lt;functional&gt;`。 #### 2. 原理 `std::bind` 的原理是基于函数对象的生成机制: - 当调用 `std::bind` 时,它创建一个内部对象(闭包),该对象存储绑定的函数和参数(包括占位符)。 - 生成的可调用对象在调用时(例如 `bound_function(args...)`),会根据占位符的顺序,将实际参数插入到绑定参数的指定位置,然后执行原始函数。 - 例如,如果绑定时使用 `_1` 作为占位符,则调用 `bound_function(x)` 会将 `x` 映射到对应位置。 - 底层实现涉及模板元编程,确保类型安全和高效调用,生成的函数对象可视为一个适配器,简化了函数柯里化(currying)或参数固定[^3][^4]。 优点包括代码复用性高、支持回调机制,但需注意性能开销(微小,取决于绑定复杂度)和与 lambda 表达式的互补性。 #### 3. 示例 下面提供几个代码示例,演示不同场景下的 `std::bind` 应用。所有示例基于 C++11 及以上编译器。 ##### 示例 1: 绑定普通函数并使用占位符 绑定一个多参数函数,固定部分参数,留一个占位符: ```cpp #include &lt;iostream&gt; #include &lt;functional&gt; // 包含 std::bind 和占位符 // 原始函数:计算三个参数的和 int add(int a, char b, double c) { return a + static_cast&lt;int&gt;(b) + static_cast&lt;int&gt;(c); } int main() { // 绑定 add 函数:固定 b 为 &#39;c&#39;(ASCII 值为 99),c 为 2.2,a 使用占位符 _1 auto bound_add = std::bind(add, std::placeholders::_1, &#39;c&#39;, 2.2); // 调用 bound_add,只需提供第一个参数(映射到 a) int result = bound_add(10); // 等同于 add(10, &#39;c&#39;, 2.2) std::cout &lt;&lt; &quot;Result: &quot; &lt;&lt; result &lt;&lt; std::endl; // 输出: Result: 112(10 + 99 + 3,注意 double 被截断) return 0; } ``` 在这个示例中,`std::bind` 生成了一个新函数 `bound_add`,它只需一个参数,其余参数已固定[^4]。 ##### 示例 2: 绑定成员函数和对象 绑定类的成员函数,需要对象实例: ```cpp #include &lt;iostream&gt; #include &lt;functional&gt; class Calculator { public: int multiply(int a, int b) { return a * b; } }; int main() { Calculator calc; // 绑定成员函数:第一个参数是对象指针(&amp;calc),后续是参数(留一个占位符) auto bound_multiply = std::bind(&amp;Calculator::multiply, &amp;calc, std::placeholders::_1, 5); // 调用 bound_multiply,只需提供第一个参数(映射到 a) int result = bound_multiply(10); // 等同于 calc.multiply(10, 5) std::cout &lt;&lt; &quot;Result: &quot; &lt;&lt; result &lt;&lt; std::endl; // 输出: Result: 50 return 0; } ``` 这里,`std::bind` 将成员函数 `multiply` 和对象 `calc` 绑定,生成了一个独立可调用对象[^3]。 ##### 示例 3: 与 lambda 表达式对比 `std::bind` 常用于简化 lambda 表达式场景;以下是等价实现: ```cpp #include &lt;iostream&gt; #include &lt;functional&gt; void printSum(int a, int b) { std::cout &lt;&lt; &quot;Sum: &quot; &lt;&lt; a + b &lt;&lt; std::endl; } int main() { // 使用 std::bind auto bound_print = std::bind(printSum, std::placeholders::_1, 100); bound_print(50); // 输出: Sum: 150 // 等价 lambda 表达式 auto lambda_print = [](int a) { printSum(a, 100); }; lambda_print(50); // 输出: Sum: 150 return 0; } ``` 此示例展示 `std::bind` 如何替代简单 lambda,但在复杂逻辑中 lambda 可能更灵活[^2]。 #### 总结 `std::bind` 是 C++ 中实现函数适配和参数绑定的高效工具,通过生成新的可调用对象,支持部分参数固定、成员函数绑定等场景,提升了代码的模块化和可维护性[^1][^2][^3]。它原理基于闭包和占位符机制,适用于回调、事件处理等模式。注意,在现代 C++ 中,lambda 表达式常作为替代,但 `std::bind` 在处理复杂绑定或遗留代码时仍具优势。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值