C++11 包装器function和bind

这篇具有很好参考价值的文章主要介绍了C++11 包装器function和bind。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.function包装器

在C++11中,可以使用std::function来创建函数包装器。std::function是一个通用的函数对象,可以存储、复制和调用各种可调用对象,包括函数指针、函数对象、Lambda表达式等。function包装器也叫作适配器。function本质是一个类模板,也是一个包装器。

ret = func(x);

上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能是Lambda表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!为什么呢?我们继续往下看

template<class F, class T>
T useF(F f, T x) {
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f(double i) {
    return i / 2;
}

struct Functor {
    double operator()(double d) {
        return d / 3;
    }
};

int main() {// 函数名
    cout << useF(f, 11.11) << endl;
    // 函数对象
    cout << useF(Functor(), 11.11) << endl;
    // lamber表达式
    cout << useF([](double d) -> double { return d / 4; }, 11.11) << endl;
    return 0;
}

输出结果如下:

count:1
count:00EDC140
5.555
count:1
count:00EDC144
3.70333
count:1
count:00EDC148
2.7775

可以发现,三个count的地址都不同,因此可以发现useF函数模板实例化了三份。

function使用方式

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
#include <functional>
int f(int a, int b){
	return a + b;
}

struct Functor{
public:
	int operator() (int a, int b){
		return a + b;
	}
};

class Plus{
public:
	static int plusi(int a, int b){
		return a + b;
	}
	double plusd(double a, double b){
		return a + b;
	}
};

1. 函数指针

// 函数名(函数指针)
std::function<int(int, int)> func1 = f;
cout << func1(1, 2) << endl;

在这个示例中,我们将函数f的指针传递给std::function的构造函数来创建一个函数包装器func1。由于f是一个函数指针,它的类型与std::function<int(int, int)>匹配,它接受两个int类型的参数并返回一个int类型的值。我们可以通过func1来调用包装的函数,并传递参数。

2. 函数对象

// 函数对象
std::function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;

在这个示例中,我们创建了一个函数对象Functor的实例,并将其传递给std::function的构造函数来创建函数包装器func2。由于Functor是一个可调用的对象(定义了operator()函数),它的类型与std::function<int(int, int)>匹配。我们可以通过func2来调用包装的函数对象,并传递参数。

3. Lambda表达式

// Lambda表达式
std::function<int(int, int)> func3 = [](const int a, const int b) { return a + b; };
cout << func3(1, 2) << endl;

在这个示例中,我们使用Lambda表达式创建了一个匿名函数,并将其传递给std::function的构造函数来创建函数包装器func3。Lambda表达式的类型由编译器推导出来,并与std::function<int(int, int)>匹配。我们可以通过func3来调用包装的Lambda函数,并传递参数。

4. 类的静态成员函数

// 类的静态成员函数
std::function<int(int, int)> func4 = &Plus::plusi;
cout << func4(1, 2) << endl;

在这个示例中,我们使用&Plus::plusi来获取Plus类的静态成员函数plusi的指针,并将其传递给std::function的构造函数来创建函数包装器func4。静态成员函数不依赖于类的实例,因此不需要额外的对象参数。我们可以通过func4来调用包装的静态成员函数,并传递参数。

5. 类的普通成员函数

//类的普通成员函数
std::function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl;

在这个示例中,我们使用&Plus::plusd来获取Plus类的普通成员函数plusd的指针,并将其传递给std::function的构造函数来创建函数包装器func5普通成员函数依赖于类的实例,因此我们需要额外传递一个类对象作为参数。我们可以通过func5来调用包装的普通成员函数,并传递类对象和其他参数。

有了包装器,如何解决模板的效率低下,实例化多份的问题呢?

#include <functional>
#include <iostream>
using namespace std;

template<class F, class T>
T useF(F f, T x) {
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f(double i) {
    return i / 2;
}

struct Functor {
    double operator()(double d) {
        return d / 3;
    }
};

int main() {// 函数名
    std::function<double(double)> func1 = f;
    cout << useF(func1, 11.11) << endl;
    // 函数对象
    std::function<double(double)> func2 = Functor();
    cout << useF(func2, 11.11) << endl;
    // Lambda表达式
    std::function<double(double)> func3 = [](double d) -> double { return d / 4; };
    cout << useF(func3, 11.11) << endl;
    return 0;
}

输出结果:

count:1
count:0071E4C8
5.555
count:2
count:0071E4C8
3.70333
count:3
count:0071E4C8
2.7775

结果可以发现三个count地址都一样,通过function只实例化出了一份。

包装器的其他一些场景:

150. 逆波兰表达式求值 - 力扣(LeetCode)

使用包装器以前的解法:

class Solution {
public:
    int evalRPN(vector<string> &tokens) {
        stack<int> st;
        for (auto &str: tokens) {
            if (str == "+" || str == "-" || str == "*" || str == "/") {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                switch (str[0]) {
                    case '+':
                        st.push(left + right);
                        break;
                    case '-':
                        st.push(left - right);
                        break;
                    case '*':
                        st.push(left * right);
                        break;
                    case '/':
                        st.push(left / right);
                        break;
                }
            } else {
                // 1、atoi itoa
                // 2、sprintf scanf
                // 3、stoi to_string C++11
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

使用包装器的解法:

class Solution {
public:
    int evalRPN(vector<string> &tokens) {
        stack<long> st;
        unordered_map<string, function<long(long, long)>> opFuncMap = {
                {"+", [](long x, long y) -> long { return x + y; }},
                {"-", [](long x, long y) -> long { return x - y; }},
                {"*", [](long x, long y) -> long { return x * y; }},
                {"/", [](long x, long y) -> long { return x / y; }}};

        for (auto &str: tokens) {
            if (opFuncMap.find(str) != opFuncMap.end()) {
                long right = st.top();
                st.pop();
                long left = st.top();
                st.pop();

                st.push(opFuncMap[str](left, right));
            } else {
                st.push(stoll(str));
            }
        }

        return st.top();
    }
};

2.bind

std::bind是C++标准库中的一个函数模板,位于<functional>头文件中。它用于创建函数对象(也称为函数绑定器),可以将一个可调用对象与其参数绑定起来,形成一个新的可调用对象。

原型如下:

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);

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对 象来“适应”原对象的参数列表。 调用bind的一般形式:auto newCallable = bind(callable,arg_list);

其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

arg_list中的参数可能包含形如n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:1为newCallable的第一个参数,_2为第二个参数,以此类推。

1. 绑定函数指针

#include <functional>
#include <iostream>

void foo(int a, int b) {
    std::cout << "Sum: " << (a + b) << std::endl;
}

int main() {
    auto boundFunc = std::bind(foo, 10, 20);
    boundFunc(); // 调用绑定的函数指针
    return 0;
}

std::bind将函数指针foo与参数1020绑定起来,创建了一个新的函数对象boundFunc。调用boundFunc()会调用原始函数foo,并传递绑定的参数。输出结果为"Sum: 30"。

2. 绑定成员函数

#include <functional>
#include <iostream>

class MyClass {
public:
    void printSum(int a, int b) {
        std::cout << "Sum: " << (a + b) << std::endl;
    }
};

int main() {
    MyClass obj;
    auto boundFunc = std::bind(&MyClass::printSum, &obj, 10, 20);
    boundFunc(); // 调用绑定的成员函数
    return 0;
}

std::bind将成员函数printSum与对象指针&obj和参数1020绑定起来,创建了一个新的函数对象boundFunc。调用boundFunc()会调用原始的成员函数printSum,并传递绑定的对象和参数。输出结果为"Sum: 30"。

3. 绑定成员函数和占位符

#include <functional>
#include <iostream>

class MyClass {
public:
    void printSum(int a, int b) {
        std::cout << "Sum: " << (a + b) << std::endl;
    }
};

int main() {
    MyClass obj;
    auto boundFunc = std::bind(&MyClass::printSum, &obj, std::placeholders::_1, 20);
    boundFunc(10); // 调用绑定的成员函数,传递部分参数
    return 0;
}

std::bind将成员函数printSum与对象指针&obj和一个占位符std::placeholders::_1(表示第一个参数)以及常数20绑定起来,创建了一个新的函数对象boundFunc。调用boundFunc(10)会调用原始的成员函数printSum,传递10作为第一个参数和绑定的常数20作为第二个参数。输出结果为"Sum: 30"。

4. 参数重排和缺省参数

#include <functional>
#include <iostream>

void foo(int a, int b, int c) {
    std::cout << "Sum: " << (a + b * c) << std::endl;   //230
}

int main() {
    auto boundFunc = std::bind(foo, std::placeholders::_3, std::placeholders::_1, std::placeholders::_2);
    boundFunc(10, 20, 30); // 调用绑定的函数,重排参数顺序    
    return 0;
}

std::bind将函数foo与占位符std::placeholders::_3std::placeholders::_1std::placeholders::_2绑定起来,创建了一个新的函数对象boundFunc。调用boundFunc(10, 20, 30)会调用原始的函数foo,参数按照重排的顺序传递,a绑定第三个参数30,b绑定第1个参数10,c绑定第二个参数20。即30+10*20 = 230。

std::bind还支持对绑定的参数进行缺省设置,例如std::bind(foo, _1, _2, 100),其中100是一个固定的常数作为缺省参数。

function和bind配合使用

使用举例文章来源地址https://www.toymoban.com/news/detail-498875.html

#include <functional>
#include <iostream>
#include <map>
#include <string>
using namespace std;
using namespace placeholders;

int Plus(int a, int b) {
    return a + b;
}

int Mul(int a, int b, int c, double rate) {
    return a + b + c * rate;
}

class Sub {
public:
    int sub(int a, int b) {
        return a - b;
    }
};

int main() {
    // 调整顺序 -- 鸡肋
    // _1 _2.... 定义在placeholders命名空间中,代表绑定函数对象的形参,
    // _1,_2...分别代表第一个形参、第二个形参...
    function<int(int, int)> funcPlus = Plus;
    //function<int(Sub, int, int)> funcSub = &Sub::sub;
    function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), _2, _1);
    function<int(int, int, int)> funcMul = bind(Mul, _3, _1, _2, 1.5);

    cout << funcPlus(1, 2) << endl;  //3
    cout << funcSub(1, 2) << endl;   //1
    cout << funcMul(3, 2, 1) << endl;//a = 1, b = 3 c = 2 * 1.5   1+3+2*1.5=7
 
    return 0;
}
int main() {
    map<string, function<int(int, int)>> opFuncMap = {
            {"+", Plus},
            {"-", bind(&Sub::sub, Sub(), _1, _2)},
            {"*", bind(Mul, _2, _1, 1, 2)},//这里只能有两个占位符,function的参数只有2个
    };


    cout << opFuncMap["+"](1, 2) << endl;//3
    cout << opFuncMap["-"](1, 2) << endl;//-1
    cout << opFuncMap["*"](2, 3) << endl;//2+3+1*2=7
}

到了这里,关于C++11 包装器function和bind的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++11】 包装器 | bind

    function包装器 也被叫做 适配器 C++11中function本质是 类模板, 也是一个包装器 意义在于 对可调用对象类型进行封装再适配 可调用对象:函数指针 / lambda / 仿函数 需要包含 头文件 functional 模板参数 Ret : 被调用函数的返回类型 …Args作为参数包,这个参数包中包含0到任意个模

    2024年02月12日
    浏览(40)
  • 【C++】C++11 包装器

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 function包装器 function包装器概述 function包装器使用 function包装器的实际应

    2024年04月26日
    浏览(29)
  • 【C++】C++11 ~ 包装器解析

    (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是 Scort 目前状态:大三非科班啃C++中 🌍博客主页:张小姐的猫~江湖背景 快上车🚘,握好方向盘跟我有一起打天下嘞! 送给自己的一句鸡汤🤔: 🔥真正的大师永远怀着一颗学徒的心 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏 🎉🎉

    2024年02月01日
    浏览(35)
  • 【chatgpt】使用docker运行chatglm3,原生支持工具调用(Function Call)、代码执行(Code Interpreter)和 Agent 任务,可以本地运行啦

    https://github.com/THUDM/ChatGLM3 介绍 ChatGLM3-6B 是 ChatGLM 系列最新一代的开源模型,在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上,ChatGLM3-6B 引入了如下特性: 更强大的基础模型: ChatGLM3-6B 的基础模型 ChatGLM3-6B-Base 采用了更多样的训练数据、更充分的训练步数

    2024年02月05日
    浏览(48)
  • 【C++11】lambda表达式 包装器

    在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法: 如果待排序元素为自定义类型,需要用户定义排序时的比较规则: 如果仿函数命名比较规范的话,像上面的命名方式的话那还好,如果遇到了像cmp1 cmp2 cmp3…这种命名方式而且还没有注释的话可以

    2024年02月12日
    浏览(43)
  • 【C++】C++11:线程库和包装器

    C++11最后一篇文章 文章目录 前言 一、线程库 二、包装器和绑定 总结 上一篇文章中我们详细讲解了lambda表达式的使用,我们今天所用的线程相关的知识会大量的用到lambda表达式,所以对lambda表达式还模糊不清的可以先将上一篇文章看明白。 一、线程库 在 C++11 之前,涉及到

    2024年02月10日
    浏览(47)
  • 【C++干货铺】C++11新特性——lambda表达式 | 包装器

    ========================================================================= 个人主页点击直达:小白不是程序媛 C++系列专栏:C++干货铺 代码仓库:Gitee ========================================================================= 目录 C++98中的排序 lambda表达式 lambda表达式语法 表达式中的各部分说明 lambda表达式的使

    2024年01月21日
    浏览(45)
  • 【C++进阶】C++11(下)可变参数模板&lambda表达式&包装器

    我们紧接着上一节的讲解来进行 C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧

    2024年04月11日
    浏览(90)
  • 【C++】 C++11(右值引用,移动语义,bind,包装器,lambda,线程库)

    C++11是C++语言的一次重大更新版本,于2011年发布,它包含了一些非常有用的新特性,为开发人员提供了更好的编程工具和更好的编程体验,使得编写高效、可维护、安全的代码更加容易。 一些C++11的新增特性包括: 强制类型枚举,使得枚举类型的通常行为更加可靠和容易控制

    2024年02月10日
    浏览(43)
  • Win11使用本地账户可以远程连接,使用微软账户提示用户账户无效解决办法

    当使用PC、安卓客户端或IPad客户端RD Client访问已经配置好远程桌面的主机时,如果使用Microsoft账户登录可能会出现用户账户无效。明明远程机器用的就是这个Microsoft账户,而且有访问权限,而且密码确定无误,却仍然无法登录。 这可能是因为:你从一开始就使用了PIN登录远程

    2024年02月15日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包