浅谈c++11闭包的实现

什么是闭包

一个函数,带上了一个状态,就变成了闭包了。那什么叫 “带上状态” 呢? 意思是这个闭包有属于自己的变量,这些个变量的值是创建闭包的时候设置的,并在调用闭包的时候,可以访问这些变量。

函数是代码,状态是一组变量,将代码和一组变量捆绑 (bind) ,就形成了闭包。

闭包的状态捆绑,必须发生在运行时。

仿函数:重载 operator()

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <map>

class MyFunctor
{
public:
    MyFunctor(int temp): round(temp) {}
    int operator()(int temp) {return temp + round; }
private:
    int round;
};

void mytest()
{
    int round = 2;
    MyFunctor f(round);
    std::cout << "result: " << f(1) << std::endl; // operator()(int temp)

    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

std::bind绑定器

在C++中,可调用实体主要包括:函数、函数指针、函数引用、可以隐式转换为函数指定的对象,或者实现了opetator()的对象。

C++11中,新增加了一个std::function类模板,它是对C++中现有的可调用实体的一种类型安全的包裹。通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们。

std::function对象最大的用处就是在实现函数回调,使用者需要注意,它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>

void func(void)
{// 普通全局函数
    std::cout << __FUNCTION__ << std::endl;
}

class Foo
{
public:
    static int foo_func(int a)
    {// 类中的静态函数
        std::cout << __FUNCTION__ << "(" << a << ")->: ";
        return a;
    }
};

class Bar
{
public:
    int operator ()(int a)
    {// 仿函数
        std::cout << __FUNCTION__ << "(" << a << ")->: ";
        return a;
    }
};

void mytest()
{
    // std::function对象最大的用处就是在实现函数回调,使用者需要注意,它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较。

    // 绑定一个普通函数
    std::function< void(void) > f1 = func;
    f1();

    // 绑定类中的静态函数
    std::function<int(int)> f2 = Foo::foo_func;
    std::cout << f2(11) << std::endl;

    // 绑定一个仿函数
    Bar obj;
    std::function<int(int)> f3 = obj;
    std::cout << f3(222) << std::endl;

    /*
     运行结果:
     func
     Foo::foo_func(11)->: 11
     Bar::operator ()(222)->: 222
    */

    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

std::bind

std::bind是这样一种机制,它可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体,这种机制在回调函数的使用过程中也颇为有用。

C++98中,有两个函数bind1st和bind2nd,它们分别可以用来绑定functor的第一个和第二个参数,它们都是只可以绑定一个参数,各种限制,使得bind1st和bind2nd的可用性大大降低。

在C++11中,提供了std::bind,它绑定的参数的个数不受限制,绑定的具体哪些参数也不受限制,由用户指定,这个bind才是真正意义上的绑定。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>

void func(int x, int y)
{
    std::cout << x << " " << y << std::endl;
}

void mytest()
{
    std::bind(func, 1, 2)();
    std::bind(func, std::placeholders::_1, 2)(1);
    func(1, 2);

    // std::placeholders 表示的是占位符
    // std::placeholders::_1是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代。
    std::bind(func, 2, std::placeholders::_1)(1);
    std::bind(func, 2, std::placeholders::_2)(1, 2);
    std::bind(func, std::placeholders::_1, std::placeholders::_2)(1, 2);
    std::bind(func, std::placeholders::_3, std::placeholders::_2)(1, 2, 3);

    //std::bind(func, 2, std::placeholders::_2)(1); // err, 调用时没有第二个参数

    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

std::bind和std::function配合使用

通过std::bind和std::function配合使用,所有的可调用对象均有了统一的操作方法

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>

class Test
{
public:
    int i; // 非静态成员变量

    void func(int x, int y)
    { // 非静态成员函数
        std::cout << x << " " << y << std::endl;
    }
};

void mytest()
{
    Test obj; // 创建对象
    // 绑定非静态成员函数
    std::function<void(int, int)> f1 = std::bind(&Test::func, &obj, std::placeholders::_1, std::placeholders::_2);
    f1(1, 2); // 输出: 1 2

    obj.i = 10;
    // 绑定非静态成员变量
    std::function<int &()> f2 = std::bind(&Test::i, &obj);
    f2() = 123;  // obj.i = 123;
    std::cout << "obj.i: " << obj.i << std::endl;

    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

以上就是浅谈c++11闭包的实现的详细内容,更多关于c++11闭包的实现的资料请关注我们其它相关文章!

时间: 2021-06-09

C/C++编写推箱子小游戏

本文实例为大家分享了C/C++编写推箱子小游戏的具体代码,供大家参考,具体内容如下 我们用' #'来代表墙,'O'来代表箱子,' * '代表终点,'@'代表箱子已经到达终点,'S来表示人'. 注意:W,A,S,D为方向键,而且要在英文格式下 运行示例: 以下为完整代码: /* 推箱子(示例二) */ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <conio.h> i

C++设计模式之工厂方法模式的实现及优缺点

工厂方法模式是在简单工厂模式的缺点上进行优化的,我们都知道,在简单工厂模式中,要去增加或者减少一个产品的类型,都需要修改工厂中的if-else判断.这样子显然不符合我们代码开发中的开闭原则,拓展起来也是非常麻烦的 工厂方法模式 = 简单工厂模式 + "开闭原则" 定义思想:工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的就是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类 优点: 不需要记住具体的类名,

C++设计模式之简单工厂模式的实现示例

前言 在我们要使用一个对象时,就必须通过类来实例化对象,也就是需要new一个对象.在new的过程是非常复杂的,要经过读文件->解析文本->创建对象->给属性设值等过程.而引入工厂模式,就是将创建类的这个步骤又工厂来帮我们完成,我们只需要去使用工厂里创建好的类即可.在使用工厂时,我们需要让工厂知道我们想要的一个对象,所以我们可以通过传参的方式去告诉工厂我们的需求 定义思想:在简单工厂模式中,可以根据参数的不同返回不同类的实例.简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通

C++ 智能指针的魅力你都了解吗

前情提要 我们知道除了静态内存和栈内存外,每个程序还有一个内存池,这部分内存被称为自由空间或者堆.程序用堆来存储动态分配的对象即那些在程序运行时分配的对象,当动态对象不再使用时,我们的代码必须显式的销毁它们. 在C++中,动态内存的管理是用一对运算符完成的:new和delete,ne:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete指向一个动态独享的指针,销毁对象,并释放与之关联的内存. 动态内存管理经常会出现两种问题:一种是忘记释放内存,会造成内存泄漏:一种是尚有指针引用

详解C++编译器优化技术

前言 注1:vc6.vs没有提供编译选项来关闭该优化,无论是debug还是release都会进行RVO和复制省略优化 注2:vc6.vs2005以下及vs2005+ Debug上不支持NRVO优化,vs2005+ Release支持NRVO优化 注3:g++支持这三种优化,并且可通过编译选项:-fno-elide-constructors来关闭优化 RVO #include <stdio.h> class A { public: A() { printf("%p construct\

推荐几款C/C++的编译器、编译环境(非常全面的比较)

C/C++编译器有哪些? 首先是如雷贯耳的这几位仁兄,MSVC.GCC.Cygwin.MingW(Cygwin和MingW的英文发音),另外还有些小众和新秀,像ICC(Intel C/C++ Compiler).BCC(Borland C/C++ Compiler,快销声匿迹了).RVCT(ARM的汇编/C/C++编译器,内置在ARM的IDE--RVDS中).Pgi编译器--其实有一大串,我们只要熟悉常用的最强大的几款就可以了. 主流C/C++编译器|编译环境简介 MSVC MSVC是微软Win

C++实现AVL树的完整代码

AVL树的介绍 AVL树是一种自平衡的二叉搜索树,它通过单旋转(single rotate)和双旋转(double rotate)的方式实现了根节点的左子树与右子树的高度差不超过1,.这有效的降低了二叉搜索树的时间复杂度,为O(log n).那么,下面小编将详细介绍C++实现AVL树的代码.最后一步提供可靠的代码实现 这里先粘贴代码 给大家的忠告,一定要及时去实现,不然之后再实现要花更多的时间 /* *平衡二叉树应该有些功能 *插入 删除 查找 *前序遍历 中序遍历 后序遍历 层次遍历 *统计结

c++多线程为何要使用条件变量详解

先看示例1: #include <iostream> #include <windows.h> #include <mutex> #include<deque> #include <thread> using namespace std; int nmax = 20; std::deque<int> m_que; std::mutex mymutex; //生产者 void producterex() { int i = 1; whi

详解C++右值引用

概述 在C++中,常量.变量或表达式一定是左值(lvalue)或右值(rvalue). 左值:非临时的(具名的,可在多条语句中使用,可以被取地址).可以出现在等号的左边或右边.可分为非常量左值和常量左值. 右值:临时的(不具名的,只在当前语句中有效,不能取地址).只能出现在等号的右边.可分为非常量右值和常量右值. 左值引用:对左值的引用就是左值引用.可分为非常量左值引用和常量左值引用. 注:常量左值引用是"万能"的引用类型,可以绑定到所有类型的值,包括非常量左值.常量左值.非常量右值和

C++11右值引用和转发型引用教程详解

右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如今被称作左值引用 lvalue reference)多一个&. 如果把经由T&&这一语法形式所产生的引用类型都叫做右值引用,那么这种广义的右值引用又可分为以下三种类型: 无名右值引用 具名右值引用 转发型引用 无名右值引用和具名右值引用的引入主要是为了解决移动语义问题. 转发型引用的引

详解C++11中的右值引用与移动语义

C++11的一个最主要的特性就是可以移动而非拷贝对象的能力.很多情况都会发生对象的拷贝,有时对象拷贝后就立即销毁,在这些情况下,移动而非拷贝对象会大幅度提升性能. 右值与右值引用 为了支持移动操作,新标准引入了一种新的引用类型--右值引用,就是必须绑定到右值的引用.我们通过&&而不是&来获得右值引用.右值引用一个重要的特性就是只能绑定到将要销毁的对象. 左值和右值是表达式的属性,一些表达式生成或要求左值,而另一些则生成或要求右值.一般而言,一个左值表达式表示的是一个对象的身份,而右

详解java的值传递、地址传递、引用传递

详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前的理解,java中基本数据类型是值传递,对象是地址(引用)传递.给大家看个例子: public class ObjectTrans { public static void main(String[] args) { String name = "123"; SChange(name);

深入了解c++11 移动语义与右值引用

1.移动语义 C++11新标准中一个最主要的特性就是提供了移动而非拷贝对象的能力.如此做的好处就是,在某些情况下,对象拷贝后就立即被销毁了,此时如果移动而非拷贝对象会大幅提升性能.参考如下程序: //moveobj.cpp #include <iostream> #include <vector> using namespace std; class Obj { public: Obj(){cout <<"create obj" << e

浅谈C++左值引用和右值引用

实例如下: #include<iostream> #include<utility> #include<vector> using namespace std; int f(); int main() { vector<int>vi(100); int i=42; int &&r1=i;//error不能把右值引用绑到左值上 int &&r2=10; int &r3=i; int &r4=10;//error非

实例详解JSON取值(key是中文或者数字)方式

JSON取值(key是中文或者数字)方式详解 先准备一个json对象用于演示 var json = {'name':'zhangsan', '年龄':23, 404:'你可能迷路了'}; 1.使用JS中with关键字 with(json) { console.log(name);//输出:zhangsan console.log(年龄);//输出:23 console.log(404);//输出:404,用这种方法读取key是数字的属性,有问题 } 2.最常见的传统的读取key console.

浅析C++11中的右值引用、转移语义和完美转发

1. 左值与右值: C++对于左值和右值没有标准定义,但是有一个被广泛认同的说法:可以取地址的,有名字的,非临时的就是左值;不能取地址的,没有名字的,临时的就是右值. 可见立即数,函数返回的值等都是右值;而非匿名对象(包括变量),函数返回的引用,const对象等都是左值. 从本质上理解,创建和销毁由编译器幕后控制的,程序员只能确保在本行代码有效的,就是右值(包括立即数);而用户创建的,通过作用域规则可知其生存期的,就是左值(包括函数返回的局部变量的引用以及const对象),例如: int& fo

C++标准之(ravalue reference) 右值引用介绍

1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了CopyElision.RVO(包括NRVO)等编译器优化技术,它们可以防止某些情况下临时对象产生和拷贝.下面简单地介绍一下CopyElision.RVO,对此不感兴趣的可以直接跳过: (1)CopyElision CopyElision技术是为了防止某些不必要的临时对象产生和拷贝,例如: 复制代码 代码如下: structA{ A(

深入解读C++中的右值引用

右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一,这点从该特性的提案在C++ - State of the Evolution列表上高居榜首也可以看得出来. 从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题.从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷.从库设计者的角度讲,它给库设计者又带来了一把利器.从库使用者的角度讲,不动一兵一卒便可以获得"免费的"效率提升- 在标准C++语言中,临时量(术语为右值,因其出

C++11右值引用和std::move语句实例解析(推荐)

右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一.从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题.从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷.从库设计者的角度讲,它给库设计者又带来了一把利器.从库使用者的角度讲,不动一兵一卒便可以获得"免费的"效率提升- 下面用实例来深入探讨右值引用. 1.什么是左值,什么是右值,简单说左值可以赋值,右值不可以赋值.以下面代码为例,"A a = getA();&q