当前位置: 代码网 > it编程>编程语言>C/C++ > C++11中的包装器实战案例

C++11中的包装器实战案例

2025年11月26日 C/C++ 我要评论
引言在c++中,可调用对象类型五花八门(函数指针、仿函数、lambda表达式、类成员函数等),它们的逻辑功能十分相似,但是类型差异巨大,导致代码声明繁琐、接口适配困难、扩展性不足。而c++11通过引入

引言

在c++中,可调用对象类型五花八门(函数指针、仿函数、lambda表达式、类成员函数等),它们的逻辑功能十分相似,但是类型差异巨大,导致代码声明繁琐、接口适配困难、扩展性不足。而c++11通过引入std::function包装器,抹平了不同类型间的差异;引入std::bind包装器,灵活调整参数列表,以适配各种接口。

1.std::function

1.1.什么是std::function

std::function是c++11提供的类模板包装器,定义在<functional>头文件中,核心作用是消除不同可调用对象的类型差异。无论是普通函数、lambda还是仿函数,都能被std::function包装成同一类型。

template <class ret, class... args>
class function<ret(args...)>;
  • ret:可调用对象的返回值类型
  • args...:可调用对象的参数列表

1.2.核心用法

1.2.1.包装普通函数

普通函数是最基础的可调用对象:
代码示例:

#include <iostream>
#include <functional>
using namespace std;
int add(int a, int b)
{
	return a + b;
}
int main()
{
	//包装add函数,类型匹配int<int, int>
	function<int(int, int)> func_add = add;
	cout << "1 + 2 = " << func_add(1, 2) << endl;
	return 0;
}

运行结果:

1.2.2.包装仿函数

仿函数是重载operator()的类实例
代码示例:

#include <iostream>
#include <functional>
using namespace std;
struct sub {
	int operator()(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	function<int(int, int)> func_sub = sub();
	cout << "5 - 3 = " << func_sub(5, 3) << endl;
	return 0;
}

运行结果:

1.2.3.包装lambda表达式

lambda是匿名函数对象,function能直接与之匹配
代码示例:

#include <iostream>
#include <functional>
using namespace std;
int main()
{
	function<int(int, int)> func_mul = [](int a, int b) {return a * b;};
	cout << "3 * 4 = " << func_mul(3, 4) << endl;
	return 0;
}

运行结果:

1.2.4.包装类成员函数

包装类成员函数需要额外处理this指针,function需在参数列表中显式声明this对应的对象类型(指针或引用);
代码示例:

#include <iostream>
#include <functional>
using namespace std;
class divide {
public:
	//非静态成员函数,参数隐含this指针
	double div(double a, double b)
	{
		return a / b;
	}
	//静态成员函数,不含this指针
	static double div_static(double a, double b)
	{
		return a / b;
	}
};
int main()
{
	divide d;
	//包装非静态成员函数,包含this指针
	function<double(divide*, double, double)> func_div = &divide::div;//非静态成员函数必须取地址,不能隐式类型转换
	cout << "func_div: " << "12.2 / 2 = " << func_div(&d, 12.2, 2) << endl;
	//包装静态成员函数,不包含this指针
	function<double(double, double)> func_stat_div = divide::div_static;//取地址可加可不加
	cout << "func_stat_div: " << "12.2 / 2 = " << func_stat_div(12.2, 2) << endl;
	return 0;
}

运行结果:

1.2.5.空包装器

若std::function是空包装器(未绑定任何可调用对象),调用时会抛异常(std::bad_function_call),注意捕获。
代码示例:

#include <iostream>
#include <functional>
using namespace std;
int main()
{
	function<int(int, int)> func_empty;
	try {
		func_empty(3, 4);
	}
	catch (const bad_function_call& e) {
		cout << "error: " << e.what() << endl;
	}
	return 0;
}

运行结果:

1.3.什么时候适合用std::function

  • 作为容器元素:如map<string,function<arg...>>,实现字符串-函数映射
  • 作为函数参数/返回值:传递回调函数时,不需要关注函数的具体类型(函数指针/仿函数/lambda),匹配函数签名(返回值类型、参数类型和数量)即可。
  • 简化类型声明:替代复杂的函数指针类型,如:int(*)(int ,int),代码更简洁。

2.std::bind

2.1.什么是std::bind

std::bind是c++11提供的函数模板,定义在<functional>头文件中,核心作用是调整可调用对象的参数列表(绑定固定参数、重新排序参数)

//simple(1)	
template <class fn, class... args>
  /* unspecified */ bind (fn&& fn, args&&... args);
//with return type (2)	
template <class ret, class fn, class... args>
  /* unspecified */ bind (fn&& fn, args&&... args);

一般形式:auto newcallable = bind(callable, arg_list)

  • callable:原可调用对象
  • arg_list:参数列表
  • 参数列表中,placeholders::_n(如_1、_2)是占位符,表示新函数的第n个参数,未用占位符的参数会被“固定绑定”。

2.2.核心用法

2.2.1.绑定固定参数(减少参数)

假设我们有一个计算乘法的函数mul,如果我们想固定其中一个因数a为100, 只用输入另一个因数b,此时可用bind实现
代码示例:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
int mul(int a, int b)
{
	return a * b;
}
int main()
{
	//绑定_a == 100, 新函数第一个参数_1传给b
	auto mul_fixed_a = bind(mul, 100, _1);
	cout << "100 * 5 = " << mul_fixed_a(5) << endl;
	////绑定_b == 200, 新函数第一个参数_1传给a
	auto sub_fixed_b = bind(mul, _1, 200);
	cout << "3 * 200 = " << mul_fixed_a(3) << endl;
	return 0;
}

运行结果:

2.2.2.重排参数顺序

如果要调整函数参数顺序,可以通过调整占位符顺序实现
代码示例:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
int sub(int a, int b)
{
    return a - b;
}
int main() {
    // 原函数是 a - b,重排后变成 b - a(_2 是新函数第2个参数,_1 是第1个)
    auto sub_swap = bind(sub, _2, _1);
    cout << "5 - 10 = " << sub_swap(10, 5) << endl; // 输出:(5-10)*10=-50
    return 0;
}

运行结果:

2.2.3.结合成员函数使用

非静态成员函数中参数包含隐藏的this指针,因此bind绑定时需要包含该类的引用或指针参数
代码示例:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
class calculator {
public:
    double multiply(double a, double b) {
        return a * b;
    }
};
int main() {
    calculator calc;
    // 绑定 calc 作为 this 指针,_1 和 _2 是新函数的两个参数
    auto calc_mul = bind(&calculator::multiply, &calc, _1, _2);
    cout << "3 * 4 = " << calc_mul(3, 4) << endl; // 输出:12
    return 0;
}

运行结果:

2.3.什么时候适合用std::bind

  • 适配参数不匹配的接口:比如一个第三方库函数需要传三个参数,但我的函数只有两个参数,这时可以用bind绑定一个参数,即可适配
  • 简化重复调用:若某函数频繁用相同参数调用,可以用bind绑定,简化函数

3.实战案例

3.1.逆波兰表达式求值(function + map优化版本)

function + map --> “操作符 - 函数”映射
代码示例:

#include <stack>
#include <vector>
#include <string>
#include <functional>
#include <map>
using namespace std;
class solution {
public:
    int evalrpn(vector<string>& tokens) {
        stack<int> st;
        // 用 map 存储“操作符-function”映射
        map<string, function<int(int, int)>> op_map = {
            {"+", [](int a, int b) { return a + b; }},
            {"-", [](int a, int b) { return a - b; }},
            {"*", [](int a, int b) { return a * b; }},
            {"-", [](int a, int b) { return a - b; }},
            {"/", [](int a, int b) { return a / b; }},
            {"%", [](int a, int b) { return a % b; }}
        };
        for (auto& str : tokens) {
            if (op_map.count(str)) { // 匹配到操作符
                int right = st.top(); st.pop();
                int left = st.top(); st.pop();
                // 调用对应的 function
                st.push(op_map[str](left, right));
            }
            else {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

3.2.复利计算

在计算复利时,需要传入三个参数:利率、本金、年限,如果我们想固定利率和年限,只输入本金,则可用bind简化函数。
代码示例:

#include <functional>
#include <iostream>
using namespace std;
using namespace placeholders;
int main() {
    // 复利计算 lambda:rate=年利率,money=本金,year=年限,返回利息
    auto calc_interest = [](double rate, double money, int year) -> double {
        double ret = money;
        for (int i = 0; i < year; i++) {
            ret += ret * rate; // 每年复利
        }
        return ret - money; // 利息 = 最终金额 - 本金
    };
    // 用 bind 固定利率和年限,生成专用函数(只需传入本金)
    function<double(double)> interest_3y_1_5 = bind(calc_interest, 0.015, _1, 3);  // 3年1.5%利率
    function<double(double)> interest_5y_2_0 = bind(calc_interest, 0.020, _1, 5);  // 5年2.0%利率
    function<double(double)> interest_10y_2_5 = bind(calc_interest, 0.025, _1, 10); // 10年2.5%利率
    // 调用简化版函数
    cout << "100万 3年1.5%利息:" << interest_3y_1_5(1000000) << endl; // 约45678.38
    cout << "100万 5年2.0%利息:" << interest_5y_2_0(1000000) << endl; // 约104080.80
    cout << "100万 10年2.5%利息:" << interest_10y_2_5(1000000) << endl; // 约280084.50
    return 0;
}

运行结果:

4.注意事项

  • function非静态成员函数时,由于非静态成员函数参数中隐藏了this指针,因此function参数列表必须包含this指针对应的对象类型(指针或引用),否则编译报错。
  • bind占位符在命名空间std::placeholders中,使用前记得显式声明using namespace std::placeholders
  • 若用bind绑定引用参数,需用ref()cref()包装,否则bind会默认按值传递。
void print(int& x) { x++; cout << x << endl; }
int main() {
    int a = 10;
    auto func = bind(print, ref(a)); // 用 ref 绑定引用
    func(); // 输出 11,a 被修改为 11
    return 0;
}
  • 包装器底层是函数对象封装,无额外性能消耗,不必担心效率问题。

结语

std::function 和 std::bind 作为 c++11 引入的强大工具,共同为处理复杂的函数调用场景带来了革命性的简化:它们消除了不同可调用对象(如普通函数、lambda 表达式、成员函数)之间的类型壁垒,使得函数的传递、存储和调用变得前所未有的统一和灵活。开发者无需再为每种回调类型定义繁琐的函数指针或适配器,std::function 提供了一个通用的 “容器”,而 std::bind 则像一个万能的 “适配器”,能够轻松调整函数签名,固定参数或重排参数顺序,极大地提升了代码的表达力和可维护性。

到此这篇关于c++11中的包装器的文章就介绍到这了,更多相关c++ 包装器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com