C++ lambda和function

lambda表达式

lambda表达式又称为匿名表达式,是C11提出的新语法。[]存储lambda表达式要捕获的值,()内的参数为形参,可供外部调用传值。lambda表达式可以直接调用

// 1  匿名调用
   [](string name)
   {
       cout << "this is anonymous" << endl;
       cout << "hello " << name << endl;
   }("zack");

上述代码定义了一个匿名函数后直接调用。我们可以通过auto初始化一个变量存储lambda表达式

// 2 通过auto赋值
   auto fname = [](string name)
   {
       cout << "this is auto  " << endl;
       cout << "hello " << name << endl;
   };

   fname("Rolin");

通过auto定义fname,然后存储了lambda表达式,之后调用fname即可。也可以通过函数指针的方式接受lambda表达式

typedef void (*P_NameFunc)(string name);
// 3 函数指针
P_NameFunc fname2 = [](string name)
{
    cout << "this is P_NameFunc " << endl;
    cout << "hello " << name << endl;
};

fname2("Vivo");

P_NameFunc定义了fname2函数指针接受了lambda表达式。也可以通过function对象接受lambda表达式,function类是C11新增的语法。

// 4 function
    function<void(string)> funcName;
    funcName = [](string name)
    {
        cout << "this is function " << endl;
        cout << "hello " << name << endl;
    };

    funcName("Uncle Wang");

用一个function对象接受了lambda表达式,同样可以调用该function对象funcName达到调用lambda的效果。

谈谈lambda的捕获

1 值捕获

int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
//值捕获
[age, name](string name_)
{
    cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl;
}("Novia");

上述lambda表达式捕获了age和name,是以值的方式来捕获的。所以无法在lambda表达式内部修改age和name的值,如果修改age和name,编译器会报错,提示无法修改const常量,因为age和name是以值的方式被捕获的。
2 引用捕获

int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
[&age, &name](string name_)
{
    cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl;
    name = "Xiao Li";
    age = 18;
}("Novia");

[]里age和name前边添加了&,此时age和name是以引用方式捕获的。所以可以在lambda表达式中修改age和name的值。
C++的lambda表达式虽然可以捕获局部变量的引用,达到类似闭包的效果,但不是真的闭包,golang和python等语言通过闭包捕获局部变量后可以增加局部变量的声明周期,C++无法做到这一点,所以下面的调用会出现崩溃。

vector<function<void(string)>> vec_Funcs;
void use_lambda2()
{
    int age = 33;
    string name = "zack";
    int score = 100;
    string job = "softengineer";

    vec_Funcs.push_back([age, name](string name_)
                        {   cout << "this is value catch " << endl;
                            cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl; });
    //危险,不要捕获局部变量的引用
    vec_Funcs.push_back([&age, &name](string name_)
                        {   cout << "this is referenc catch" << endl;
                            cout << "age is " << age << " name is " << name << " self-name is " << name_ << endl; });
}

void use_lambda3()
{
    for (auto f : vec_Funcs)
    {
        f("zack");
    }
}

int main(){
    use_lambda2();
    use_lambda3();
}

use_lambda2中将lambda表达式存储在function类型的vector里,当use_lambda2结束后,里边的局部变量都被释放了,而vector中的lambda表达式还存储着局部变量的引用,在调用use_lambda3时调用lambda表达式,此时访问局部变量已经被释放了,所以导致程序崩溃。
3 全部用值捕获,name用引用捕获

int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
[=, &name]()
{
    cout << "age is " << age << " name is " << name << " score is " << score << " job is " << job << endl;
    name = "Cui Hua";
}();

通过=表示所有变量都以值的方式捕获,如果希望某个变量以引用方式捕获则单独在这个变量前加&。
4 全部用引用捕获,只有name用值捕获

int age = 33;
string name = "zack";
int score = 100;
string job = "softengineer";
[&, name]()
{
     cout << "age is " << age << " name is " << name << " score is " << score << " job is " << job << endl;
}();

通过&方式表示所有变量都已引用方式捕获,如果希望某个变量以值方式捕获则单独在这个变量前加=。

万能的function

我们可以用function存储形参和返回值相同的一类函数指针,可调用对象,lambda表达式等。

void use_function()
{
    list< function< void(string)> > list_Funcs;
    //存储函数对象
    list_Funcs.push_back(FuncObj());
    //存储lambda表达式
    list_Funcs.push_back([](string str)
                         { cout << "this is lambda call " << str << endl; });
    //存储全局函数
    list_Funcs.push_back(globalFun);

    for (const auto &f : list_Funcs)
    {
        f("hello zack");
    }
}

bind操作

C11同样提供了bind操作,将原函数的几个参数通过bind绑定传值,返回一个新的可调用对象。

//绑定全局函数
auto newfun1 = bind(globalFun2, placeholders::_1, placeholders::_2, 98, "worker");
//相当于调用globalFun2("Lily",22, 98,"worker");
newfun1("Lily", 22);
//多传参数没有用,相当于调用globalFun2("Lucy",28, 98,"worker");
newfun1("Lucy", 28, 100, "doctor");
auto newfun2 = bind(globalFun2, "zack", placeholders::_1, 100, placeholders::_2);
//相当于调用globalFun2("zack",33,100,"engineer");
newfun2(33, "engineer");
auto newfun3 = bind(globalFun2, "zack", placeholders::_2, 100, placeholders::_1);
newfun3("coder", 33);

placeholders表示占位符,_1表示新生成函数的第一个参数, _2表示新生成函数的第二个参数,将这些参数传递给原函数达到占位的效果,原函数的其余参数通过bind绑定固定值。
接下来定义类

class BindTestClass
{
public:
    BindTestClass(int num_, string name_) : num(num_), name(name_) {}
    static void StaticFun(const string &str, int age);
    void MemberFun(const string &job, int score);

public:
    int num;
    string name;
};

实现静态函数和成员函数

void BindTestClass::StaticFun(const string &str, int age)
{
    cout << "this is static function" << endl;
    cout << "name is " << str << endl;
    cout << "age is " << age << endl;
}
void BindTestClass::MemberFun(const string &job, int score)
{
    cout << "this is member function" << endl;
    cout << "name is " << name << endl;
    cout << "age is " << num << endl;
    cout << "job is " << job << endl;
    cout << "score is " << score << endl;
}

我们通过bind绑定静态成员函数

//绑定类的静态成员函数,加不加&都可以
// auto staticbind = bind(BindTestClass::StaticFun, placeholders::_1, 33);
auto staticbind = bind(&BindTestClass::StaticFun, placeholders::_1, 33);
staticbind("zack");

新生成的staticbind函数可以直接传递一个参数zack就完成了调用。接下来用bind绑定成员函数

BindTestClass bindTestClass(33, "zack");
// 绑定类的成员函数,一定要传递对象给bind的第二个参数,可以是类对象,也可以是类对象的指针
// 如果要修改类成员,必须传递类对象的指针
auto memberbind = bind(&BindTestClass::MemberFun, &bindTestClass, placeholders::_1, placeholders::_2);
memberbind("coder", 100);

auto memberbind2 = bind(&BindTestClass::MemberFun, placeholders::_3, placeholders::_1, placeholders::_2);
memberbind2("coder", 100, &bindTestClass);
//绑定类成员时,对象必须取地址
auto numbind = bind(&BindTestClass::num, placeholders::_1);
std::cout << numbind(bindTestClass) << endl;

当然也可以直接用function对象接受bind返回的结果

// function接受bind返回的函数
function< void(int, string)> funcbind = bind(globalFun2, "zack", placeholders::_1, 100, placeholders::_2);
funcbind(33, "engineer");

// function接受bind 成员函数
function< void(string, int)> funcbind2 = bind(&BindTestClass::MemberFun, &bindTestClass, placeholders::_1, placeholders::_2);
funcbind2("docker", 100);

function< void(string, int, BindTestClass *)> funcbind3 = bind(&BindTestClass::MemberFun, placeholders::_3, placeholders::_1, placeholders::_2);
funcbind3("driver", 100, &bindTestClass);

// function 直接接受成员函数,function的模板列表里第一个参数是类对象引用
function< void(BindTestClass &, const string &, int)> functomem = BindTestClass::MemberFun;
functomem(bindTestClass, "functomem", 88);

// function 绑定类的静态成员函数
function< void(const string &)> funbindstatic = bind(&BindTestClass::StaticFun, placeholders::_1, 33);
funbindstatic("Rolis");

lambda和bind的使用就介绍到这里
源码链接:https://gitee.com/secondtonone1/cpplearn
视频链接: https://www.bilibili.com/video/BV15S4y1Y7no

热门评论

热门文章

  1. vscode搭建windows C++开发环境

    喜欢(596) 浏览(82817)
  2. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(6071)
  3. 使用hexo搭建个人博客

    喜欢(533) 浏览(11817)
  4. Linux环境搭建和编码

    喜欢(594) 浏览(13461)
  5. Qt环境搭建

    喜欢(517) 浏览(24531)

最新评论

  1. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  2. 创建项目和编译 secondtonone1:谢谢支持
  3. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好
  4. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  5. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o
  6. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  7. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  8. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  9. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!
  10. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  11. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里
  12. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  13. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  14. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  15. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  16. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  17. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  18. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  19. 类和对象 陈宇航:支持!!!!
  20. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  21. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  22. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  23. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW

个人公众号

个人微信