可调用对象与lambda表达式

2023-10-26

可调用对象与lambda表达式


1.using

  1. 在 C++ 中 using 用于声明命名空间,使用命名空间也可以防止命名冲突。
  2. using还可以用于定义类型的别名(与typedef效果一致,但是语法略有不同),

(1)函数指针别名

case1:定义基础类型
typedef unsigned int uint_t;
using uint_t = unsigned int;

声明符也可以包含类型修饰,从而也能由基本数据类型构造出复合类型,可以使用 using 别名声明来定义类型的别名。

case2:定义函数指针别名

在定义基础类型时,通过 using 和 typedef 的语法格式发现二者的使用没有太大区别,其不同在于定义函数指针时的差别:

typedef int(*func)(int, double);//使用typedef定义函数指针 其中func为函数指针 int为其返回类型
using func = int(*)(int, double);//使用using定义函数指针 其中func为函数指针 int为其返回类型

使用 using 定义函数指针别名十分直观,将别名的名字分离到了左边,别名对应的实际类型放在了右边,可读性更好。

(2)模板定义别名

使用 typedef 重定义类似很方便,但是它有一点限制,比如无法重定义一个模板,

比如我们需要以 int 类型为 key 的 map,它可以和很多类型的 value 值进行映射,如果使用 typedef 这样直接定义就非常麻烦:

typedef map<int, string> m1;
typedef map<int, int> m2;
typedef map<int, double> m3;
//在这种情况下我们就不自觉的想到了模板:
template <typename T>
typedef map<int, T> type;	// error, 语法错误

使用 typename 不支持给模板定义别名,这个简单的需求仅通过 typedef 很难办到,需要添加一个外敷类:

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

template <typename T>
//定义外敷类
struct MyMap {
    typedef map<int, T> type;
};

int main(void) {
    MyMap<string>::type m;
    m.insert(make_pair(1, "luffy"));
    m.insert(make_pair(2, "ace"));

    MyMap<int>::type m1;
    m1.insert(1, 100);
    m1.insert(2, 200);

    return 0;
}

通过上边的例子可以直观的感觉到,需求简单但是实现起来并不容易。

在 C++11 中,新增了一个特性就是可以通过使用 using 来为一个模板定义别名,对于上面的需求可以写成这样:

template <typename T>
using mymap = map<int, T>;
#include <iostream>
#include <functional>
#include <map>
using namespace std;

template <typename T>
using mymap = map<int, T>;

int main(void) {
    // map的value指定为string类型
    mymap<string> m;
    m.insert(make_pair(1, "luffy"));
    m.insert(make_pair(2, "ace"));

    // map的value指定为int类型
    mymap<int> m1;
    m1.insert(1, 100);
    m1.insert(2, 200);

    return 0;
}

通过使用 using 给模板指定别名,就可以基于别名非常方便的给 value 指定相应的类型,这样使编写的程序更加灵活,总结:

  1. using 语法和 typedef 一样,并不会创建出新的类型,它们只是给某些类型定义了新的别名。
  2. using 相较于 typedef 的优势在于定义函数指针别名时看起来更加直观,并且可以给模板定义别名

2.可调用对象

  • 可调用对象:C++中有一类对象,可以将该类对象按照函数的方式进行调用,称之为可调用对象。
  • 对象包装器:将不同类型的可调用对象,再次进行包装类型统一,以统一的类型进行参数的传递。
  • 对象绑定器:给包装好的可调用对象,进行参数绑定(进行初始化操作)。

可调用对象有如下几种定义:

  1. 是一个函数指针

    int print(int a, double b) {
        cout << a << b << endl;
        return 0;
    }
    // 定义函数指针
    int (*func)(int, double) = &print;
    using func = void(*)(int, double);
    
  2. 是一个具有operator()成员函数类对象(仿函数)

    struct Test {
        void operator()(string msg) {//()操作符重载
            cout << "msg: " << msg << endl;
        }
    };
    
    int main(void) {
        Test test;
        test("我是要成为海贼王的男人!!!");//仿函数
        return 0;
    }
    
  3. 是一个可被转换为函数指针类对象

    using func_ptr = void(*)(int, string);
    
    struct Test {
        operator func_ptr() {//将类对象转换为函数指针
            return print2;//返回函数地址只能是print2(静态函数的函数地址)
        }
        void print1(int a, string b) {
            /* 返回的函数地址只能是print2(静态) 而不能是print1(非静态) */
            /* 这是因为静态方法是属于类的 非静态方法是属于对象的 在还没有定义对象之前这个函数是不存在的 */
            cout << a << b << endl;
        }
        static void print2(int a, string b) {
            cout << a << b << endl;
        }
    };
    
    int main(void) {
        Test test;
        test(19, "Monkey D. Luffy");// 对象转换为函数指针, 并调用
        return 0;
    }
    
  4. 是一个类成员函数指针或者类成员指针

    struct Test {
        void print1(int a, string b) {
            cout << a << b << endl;
        }
        static void print2(int a, string b) {
            cout << a << b << endl;
        }
        int m_num;
    };
    
    int main(void) {
        //1.定义类成员函数指针指向类成员函数
        //1-1.指向静态成员函数
        using func_ptr = void(*)(int, string);
        func_ptr f = &Test::print2;
        //1-2.指向非静态成员函数(在1-1的基础上指定函数作用域)
        using func_ptr = void(Test::*)(int, string);
        func_ptr f = &Test::print1;
        void (Test::*func_ptr)(int, string) = &Test::print1;//合并写法
        
        //2.类成员指针指向类成员变量
        using obj_ptr = int Test::*;
        obj_ptr ptr1 = &Test::m_num;
        //int Test::*obj_ptr = &Test::m_num;
        
        //3.通过类对象进行调用操作
        Test test;
        (test.*func_ptr)(19, "Monkey D. Luffy");//通过类成员函数指针调用类成员函数
        test.*obj_ptr = 1;//通过类成员指针初始化类成员变量
        cout << "number is: " << t.m_num << endl;
        return 0;
    }
    

在上面的例子中满足条件的这些可调用对象,对应的类型被统称为可调用类型。C++ 中的可调用类型虽然具有比较统一的操作形式,但定义方式非常多,这样在使用统一的方式保存,或者传递一个可调用对象时会十分繁琐。

现在C++11通过提供 std::functionstd::bind 统一了可调用对象的各种操作。

(1)包装器

std::function 是可调用对象的包装器(模板类型的类),可以容纳除了类成员/函数指针之外的所有可调用对象(bind绑定器协助)。

通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们。

  1. 包装 普通函数
  2. 包装 类的静态函数
  3. 包装 仿函数
  4. 包装 可转换为函数指针的对象
case1:基本用法
#include <functional>
std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
int add(int a, int b) {
    cout << a << " + " << b << " = " << a + b << endl;
    return a + b;
}

class T1 {
public:
    static int sub(int a, int b) {
        cout << a << " - " << b << " = " << a - b << endl;
        return a - b;
    }
};

using func_ptr = void(*)(int, string);

class T2 {
public:
    int operator()(int a, int b) {
        cout << a << " * " << b << " = " << a * b << endl;
        return a * b;
    }
    operator func_ptr() {//将类对象转换为函数指针
        return print2;//返回函数地址只能是print2(静态函数的函数地址)
    }
    void print1(int a, string b) {
        /* 返回的函数地址只能是print2(静态) 而不能是print1(非静态) */
        /* 这是因为静态方法是属于类的 非静态方法是属于对象的 在还没有定义对象之前这个函数是不存在的 */
        cout << a << b << endl;
    }
    static void print2(int a, string b) {
        cout << a << b << endl;
    }
};

int main(void) {
    //1.绑定一个普通函数
    function<int(int, int)> f1 = add;
    //2.绑定以静态类成员函数
    function<int(int, int)> f2 = T1::sub;
    //3.绑定一个仿函数
    T2 t;
    function<int(int, int)> f3 = t;
    //4.包装对象转换为函数指针之后 的可调用对象
    T2 t;
    function<void(int, string)> f4 = t;
    
    //函数调用
    f1(9, 3);
    f2(9, 3);
    f3(9, 3);
    f4(9, "string");
    return 0;
}

包装完成得到的对象相当于一个函数指针,和函数指针的使用方式相同,通过包装器对象就可以完成对包装的函数的调用了

case2:作为回调函数使用

因为回调函数本身就是通过函数指针实现的,使用对象包装器可以取代函数指针的作用:

使用对象包装器优点

  • 使用对象包装器std::function可将仿函数转为函数指针,通过函数指针传递在其他函数合适的位置就可以调用包装好的仿函数了。
  • 另外使用 std::function 作为函数的传入参数,可以将定义方式不相同的可调用对象进行统一的传递,这样大大增加了程序的灵活性。
class A {
public:
    /* 构造函数参数是一个包装器对象 */
    A(const function<void()>& f) : callback(f) { }
    void notify() {
        callback(); //调用通过构造函数得到的函数指针
    }
private:
    function<void()> callback;
};

class B {
public:
    void operator()() {
        cout << "我是要成为海贼王的男人!!!" << endl;
    }
};

int main(void) {
    A a(add);
    a.notify(1, 2);
    
    A a(T1::sub);
	a.notify(4, 2);
    
    B b;
    A a(b);//仿函数通过包装器对象进行包装
    a.notify();
    return 0;
}

(2)绑定器

std::bind用来将可调用对象与其参数绑定到一起。

绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。其主要有两大作用:

  1. 将可调用对象与其参数绑定成一个仿函数
  2. 将多元(参数个数为n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。

绑定器函数使用语法格式如下:

//绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
//绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
case1:绑定非类成员函数\变量

下面来看一个关于绑定器的实际使用的例子:

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

void callFunc(int x, const function<void(int)>& f) {
    /* 第二个参数为包装器类型 如果要传入实参必须为一个 可调用对象(通过绑定器获得) */
    /* 可以对output_add函数进行绑定 得到一个可调用对象(仿函数) 再将其传递给包装器 */
    /* 如果x的值为偶数 则调用包装器包装得到的 仿函数f(x) 即函数output_add被调用 */
    if (x % 2 == 0) f(x);
}

void output(int x) { cout << x << " "; }

void output_add(int x) { cout << x + 10 << " "; }

int main(void) {
    //1.使用绑定器绑定 可调用对象和参数
    auto f1 = bind(output, placeholders::_1);//绑定成功后得到一个可调用对象(仿函数)
    for (int i = 0; i < 10; ++i) callFunc(i, f1);
    cout << endl;

    auto f2 = bind(output_add, placeholders::_1);
    for (int i = 0; i < 10; ++i) callFunc(i, f2);
    cout << endl;

    return 0;
}

在上面的程序中,使用了 std::bind 绑定器,在函数外部通过绑定不同的函数,控制了最后执行的结果。

std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,在使用的时候我们并不需要关心绑定器的返回值类型,使用auto进行自动类型推导就可以了。

case2:placeholders占位符

placeholders :: _1 是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符 placeholders :: _2、placeholders :: _3、placeholders :: _4、placeholders :: _5 等……

有了占位符的概念之后,使得 std::bind 的使用变得非常灵活:

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

void output(int x, int y) { cout << x << " " << y << endl; }

int main(void) {
    //使用绑定器绑定可调用对象和参数, 并调用得到的仿函数
    //绑定普通函数的地址 并指定了两个固定的参数
    bind(output, 1, 2)();
    bind(output, placeholders::_1, 2)(10);
    bind(output, 2, placeholders::_1)(10);

    //error, 调用时没有第二个参数
    //bind(output, 2, placeholders::_2)(10);错误写法
    //调用时第一个参数10被吞掉了,没有被使用 使用绑定的值
    bind(output, 2, placeholders::_2)(10, 20);

    bind(output, placeholders::_1, placeholders::_2)(10, 20);
    bind(output, placeholders::_2, placeholders::_1)(10, 20);
    return 0;
}

std::bind 可以直接绑定函数的所有参数,也可以仅绑定部分参数。

在绑定部分参数的时候,通过使用 std::placeholders 来决定空位参数将会属于调用发生时的第几个参数。

可调用对象包装器 std::function 是不能实现对 类成员函数指针 或者 类成员指针 包装的,但是通过绑定器 std::bind 的配合之后,就可以完美的解决这个问题了。

case3:绑定类成员函数\变量
#include <iostream>
#include <functional>
using namespace std;

class Test {
public:
    void output(int x, int y) {
        cout <<  x << " " << y << endl;
    }
    int m_number = 100;
};

int main(void) {
    Test t;
    //1.绑定类成员函数
    //仿函数转包装器类型
    function<void(int, int)> f1 = bind(&Test::output, &t, placeholders::_1, placeholders::_2);
    //2.绑定类成员变量(公共)
    //仿函数转为包装器类型
    function<int&(void)> f2 = bind(&Test::m_number, &t);
    
    //3.调用
    f1(520, 1314);
    f2() = 2333;
    cout << "t.m_number: " << t.m_number << endl;
    return 0;
}

在用绑定器绑定类成员函数或者成员变量的时候需要将它们所属的实例对象一并传递到绑定器函数内部

  • f1类型是function<void(int, int)>,通过std::bind将类成员函数output地址与对象t绑定,转化为一个仿函数存储到对象f1中
  • 使用绑定器绑定的类成员变量m_number得到的仿函数被存储到了类型为function<int&(void)>的包装器对象f2中,并且可以在需要的时候修改这个成员。其中int是绑定的类成员的类型并且允许修改绑定的变量,因此需要指定为变量的引用,由于没有参数因此参数列表指定为void。

3.lambda表达式

lambda 表达式有如下的一些优点:

  1. 声明式的编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或函数对象。
  2. 简洁:避免了代码膨胀和功能分散,让开发更加高效。
  3. 在需要的时间和地点实现功能闭包,使程序更加灵活。

(1)基本用法

lambda 表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。lambda 表达式的语法形式简单归纳如下:

[capture](params) opt -> ret {body;};

其中 capture 是捕获列表,params 是参数列表,opt 是函数选项,ret 是返回值类型,body 是函数体。

  1. 捕获列表 []: 捕获一定范围内的变量

  2. 参数列表 (): 和普通函数的参数列表一样,如果没有参数参数列表可以省略不写。

    auto f = [](){return 1;}	// 没有参数, 参数列表为空
    auto f = []{return 1;}		// 没有参数, 参数列表省略不写
    
  3. opt 选项, 不需要可以省略

    • mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
    • exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();
  4. 返回值类型:C++11 中lambda 表达式的返回值是通过返回值后置语法来定义的。

  5. 函数体:函数的实现,这部分不能省略,但函数体可以为空。

(2)捕获列表

lambda 表达式的捕获列表可以捕获一定范围内的变量,具体使用方式如下:

  1. [] - 不捕捉任何变量
  2. [&] - 捕获外部作用域中所有变量,并作为引用在函数体内使用 (按引用捕获)
  3. [=] - 捕获外部作用域中所有变量,并作为副本在函数体内使用 (按值捕获) 拷贝的副本在匿名函数体内部是只读的
  4. [=, &foo] - 按值捕获外部作用域中所有变量,按照引用捕获外部变量 foo
  5. [bar] - 按值捕获 bar 变量,同时不捕获其他变量
  6. [&bar] - 按引用捕获 bar 变量,同时不捕获其他变量
  7. [this] - 捕获当前类中的 this 指针
    • 让 lambda 表达式拥有和当前类成员函数同样的访问权限
    • 如果已经使用了 & 或者 =, 默认添加此选项

(3)使用细节:

  1. 如果在捕捉列表中指定了this,就意味在匿名函数体内部,只能够使用this指针指向的类 中的成员函数 及 成员变量。
  2. 使用=来捕捉外部变量,则所有传递的值都是拷贝的方式进行传递,并且值都是只读的属性。如果想要修改这些被拷贝来的变量,则需要在函数体前加上mutable关键字修饰。
#include <iostream>
#include <functional>
using namespace std;

class Test {
public:
    void output(int x, int y) {
        auto x1 = [] {return m_number; };            //error 没有捕获外部变量,不能使用类成员 m_number
        auto x2 = [=] {return m_number + x + y; };   //ok 以值拷贝的方式捕获所有外部变量
        auto x3 = [&] {return m_number + x + y; };   //ok 以引用的方式捕获所有外部变量
        auto x4 = [this] {return m_number; };        //ok 捕获this指针,可访问对象内部成员
        auto x5 = [this] {return m_number + x + y; };//error 捕获this指针可访问类内部成员,没有捕获到变量x|y不能访问
        auto x6 = [this, x, y] {return m_number + x + y; };  //ok 捕获 this指针 x y
        auto x7 = [this] {return m_number++; };              //ok 捕获this指针,并且可以修改对象内部变量的值
    }
    int m_number = 100;
};
int main(void) {
    int a = 10, b = 20;
    auto f1 = [] {return a; };           //error 没有捕获外部变量,因此无法访问变量a
    auto f2 = [&] {return a++; };        //ok 使用引用的方式捕获外部变量,可读写
    auto f3 = [=] {return a; };          //ok 使用值拷贝的方式捕获外部变量,可读
    auto f4 = [=] {return a++; };        //error 使用值拷贝的方式捕获外部变量,可读不能写
    auto f5 = [a] {return a + b; };      //error 使用拷贝的方式捕获了外部变量a,没有捕获外部变量b,因此无法访问变量b
    auto f6 = [a, &b] {return a + (b++); };  //ok 使用拷贝的方式捕获了外部变量a只读,使用引用的方式捕获外部变量b可读写
    auto f7 = [=, &b] {return a + (b++); };  //ok 使用值拷贝的方式捕获所有外部变量以及b的引用,b可读写,其他只读
    return 0;
}

在匿名函数内部,需要通过 lambda 表达式的捕获列表控制如何捕获外部变量,以及访问哪些变量。

默认状态下lambda表达式无法修改通过复制方式捕获外部变量,如果希望修改这些外部变量,需要通过引用的方式进行捕获

(4)返回值

很多时候,lambda 表达式的返回值非常明显,因此在C++11中允许省略 lambda 表达式的返回值。

//完整的lambda表达式定义
auto f = [](int a) -> int {
    return a+10;
};
//忽略返回值的lambda表达式定义
auto f = [](int a) {
    return a+10;
};

一般情况下,不指定 lambda 表达式的返回值,编译器会根据 return 语句自动推导返回值的类型,但需要注意的是 labmda表达式不能通过列表初始化自动推导出返回值类型

//ok 可以自动推导出返回值类型
auto f = [](int i) { 
    return i;
}
//error 不能推导出返回值类型
auto f1 = []() {
    return {1, 2};//基于列表初始化推导返回值,错误
}

(5)函数本质

使用lambda表达式捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用 mutable 选项,被mutable修饰是lambda表达式就算没有参数也要写明参数列表,并且可以去掉按值捕获的外部变量的只读(const)属性。

int a = 0;
auto f1 = [=] {return a++; };              // error, 按值捕获外部变量, a是只读的
auto f2 = [=]()mutable {return a++; };     // ok

什么通过值拷贝的方式捕获的外部变量是只读的?

  1. lambda表达式的类型在C++11中会被看做是一个带operator()的类,即仿函数
  2. 按照C++标准lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量值的。mutable 选项的作用就在于取消 operator () 的 const 属性:

因为lambda表达式在C++中会被看做是一个仿函数,因此可以使用std::function和std::bind来存储和操作lambda表达式

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

int main(void) {
    //1.包装可调用函数
    std::function<int(int)> f1 = [](int a) {return a; };
    //2.绑定可调用函数
    std::function<int(int)> f2 = bind([](int a) {return a; }, placeholders::_1);

    //3.函数调用
    cout << f1(100) << endl;
    cout << f2(200) << endl;
    return 0;
}
  • 若lambda 表达式没有捕获任何的外部变量 ,则其可以转换成一个普通的函数指针
using func_ptr = int(*)(int);
//没有捕获任何外部变量的匿名函数
func_ptr f = [](int a) {
    return a;
};
//函数调用
f(1314);

(6)使用案例:

//案例1:输出两数之和
auto f = [](int a, int b) -> int { return a + b; };
cout << f(1, 2) << endl;
//案例2:对vec数组进行升序sort排序
vector<int> vec {0, 11, 3, 19, 22, 7, 1, 5};
auto f = [](int a, int b) { return a < b; };
sort(vec.begin(), vec.end(), f);
//案例3-1:加法成员函数
#include <iostream>
using namespace std;

//使用成员函数调用
class AddNum {
public:
    AddNum(int num) : num_(num) {}
    int addNum(int x) const { return num_ + x; }
private:
    int num_;
};

int main() {
    auto add_num = AddNum(10);
    auto x = add_num.addNum(5);
    cout << "x = " << x << endl;
    return 0;
}
//案例3-2:加法仿函数
#include <iostream>
using namespace std;

//使用仿函数替代成员函数
class AddNum {
public:
    AddNum(int num) : num_(num) {}
    int operator()(int x) const { return num_ + x; }
private:
    int num_;
};

int main() {
    auto add_num = AddNum(10);
    auto x = add_num(5);
    cout << "x = " << x << endl;
    return 0;
}
//案例3-2:加法lambda表达式
#include <iostream>
using namespace std;

//使用lambda表达式仿函数
int main() {
    auto add_num = [num = 10](int x) {return num + x;};//lambda表达式替换整个AddNum类
    auto x = add_num(5);
    cout << "x = " << x << endl;
    return 0;
}

其实lambda表达式也是一个特殊的类类型,只不过不知道现在该类型的名字,编译器会帮其取名。

参考:https://subingwen.cn/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

可调用对象与lambda表达式 的相关文章

随机推荐

  • 二层组播和三层组播

    平时常常说组播 其实只是多播的另外一种叫法 多播中 因为把参与多播的所有接收者称为组 所以才有组播的说法 多播技术要比广播技术复杂的多 多播技术对一些应用很重要 比如电视会议 聊天室等 物理层多播 系统需要对网络接口进行配置 让接口识别该地
  • MATLAB行向量顺序颠倒函数 - fliplr

    fliplr A 只可用于行向量 列向量不行 实例 1 行向量 2 列向量
  • 如何使用正则表达式实现Java日志信息的抓取与收集

    首先 什么是Java日志信息 简单来说 Java应用程序在运行过程中会输出一些信息 这些信息可以用来追踪程序运行状态 调试错误等 而Java日志信息就是这些输出信息的集合 那么为什么要抓取和收集Java日志信息呢 一方面 这些信息可以帮助我
  • 失业的程序员(八):创业的要素

    一 管饭哥登场 按理说我规定我和卞工的上班时间是上午8点到10点 弹性足够大 虽曰规定 但是遵不遵守随意 原因只有一个 引用卞工的话 就两个人 考毛勤 我 很是认可 严密的考勤制度的建立是老板对员工不怎么太信任的开始 是一种等级制度的体现
  • 1.神奇的字符串之快速求和

    文章目录 前言 正题 先看第一个代码 直接循环取出每一位数 总结 前言 这个专栏是分享一些好用的数据 和一些解题比较快的小方法 会持续更新 因为博主还是计算机方向的小白 知道的东西还是很少 希望大家可以多多指教 正题 众所周知 字符串一直是
  • PyTorch实现Logistic regression

    逻辑回归 Logistic regression 回归方法是对数值型连续随机变量进行预测和建模的监督学习算法 其特点是标注的数据集具有数值型的目标变量 回归的目的是预测数值型的目标值 逻辑回归对应线性回归 旨在解决分类问题 即将模型的输出转
  • python如何对微信应用进行监听

    要在 Python 中监听微信应用 需要使用微信提供的接口和相关的第三方库 具体实现方法如下 首先需要申请微信公众平台账号并获取相应的 AppID 和 AppSecret 使用第三方库 如 itchat 来进行接口调用 Itchat 是一个
  • Git提交error: RPC failed; result=22, HTTP code = 500的解决方法

    新建了一个项目使用sourcetree提交Git时 提示错误 RPC failed result 22 HTTP code 500 这是由于上传的包过大 HTTP的头错误导致的 解决办法 在终端执行命令显示隐藏文件夹 显示全部文件 defa
  • wget -o -O和-O-有什么区别?bash改色

    wget o O和 O 有什么区别 wget o 下载过程信息存入日志文件wget o youlogname log url 下载文件放另一边 wget O 以其他名称保存下载的文件内容 输出下载过程信息wget O home ym dem
  • linux之librdkafka库安装以及将#include <librdkafka/rdkafka.h>更改为#include <rdkafka.h>调用

    公众号 嵌入式不难 本文仅供参考学习 如有错误之处 欢迎留言指正 下载源代码 使用如下命令 git clone https github com edenhill librdkafka git 切换到发布的稳定分支 刚下载下来的源代码默认在
  • -O1 -O2 -O3 优化的原理是什么?

    一般来说 如果不指定优化标识的话 gcc就会产生可调试代码 每条指令之间将是独立的 可以在指令之间设置断点 使用gdb中的 p命令查看变量的值 改变变量的值等 并且把获取最快的编译速度作为它的目标 当优化标识被启用之后 gcc编译器将会试图
  • 鸿蒙应用开发学习

    系列文章目录 第一章 HarmonyOS是什么 第二章 基础环境和开发工具 文章目录 系列文章目录 前言 一 HarmonyOS工程介绍 二 工程目录结构 三 工程目录介绍 1 entry 2 Ability 3 库文件 4 资源文件 5
  • Linux指令中touch和mkdir的区别

    在Linux中 mkdir 用于创建空的文件夹 格式 mkdir 选项 目录 选项 功能 m 默认文件目录的权限 m755 p 连续创建多层 v 显示创建过程 touch touch 是用于创建新的文件 或者修改文件的时间
  • 福昕阅读器注册码

    以下文字复制到记事本存为frpkey txt 复制到福昕阅读器的安装目录即可 FoxitReaderPro SN FRPFZ12391Modules Users 1Licensee OlivierGuilloryLicenseDate 20
  • sql中and和or的混合使用

    1 and的优先级高于or 2 使用 调整优先级 下面sql没有添加过滤条件 下面sql查出的结果是错误的 下面sql查出的结果是正确的
  • 延时函数

    Dos sleep 1 停留1秒 delay 100 停留100毫秒 Windows Sleep 100 停留100毫秒 Linux sleep 1 停留1秒 usleep 1000 停留1毫秒 每一个平台不太一样 最好自己定义一套跨平台的
  • 继续探索Roop(单张图视频换脸)的各方面:比如喜闻乐见的“加速”

    文章目录 一 Roop项目的特点 二 Roop也能加速 三 Roop更新和依赖 3 1 飞速更新 3 2 依赖问题 3 3 需要CUDA么 前两天写了 简单介绍Roop 类似SimSwap 单张图视频换脸的项目 介绍了基本安装使用 之后这个
  • [1193]ClickHouse写入常见问题: Too many parts (300)

    文章目录 一 场景及错误信息 二 报错原因 三 解决办法 扩展 一 场景及错误信息 今天使用 Datax 往 ClickHouse 同步数据时 出现如下错误 ClickHouse exception code 1002 host 10 12
  • Ubuntu22.04安装mysql集群一主一从

    Ubuntu22 04安装mysql集群 以下是在Ubuntu 22 04上安装一主一从的MariaDB集群的步骤 首先 你需要有两个 Ubuntu 22 04 的服务器 分别命名为 Server1 和 Server2 这两个服务器都需要安
  • 可调用对象与lambda表达式

    可调用对象与lambda表达式 OVERVIEW 可调用对象与lambda表达式 1 using 1 函数指针别名 case1 定义基础类型 case2 定义函数指针别名 2 模板定义别名 2 可调用对象 1 包装器 case1 基本用法