当前位置: 首页 > news >正文

C++进阶——C++11_{ }初始化_lambda_包装器

目录

1、{ }初始化

1.1 C++98的{ }

1.2 C++11的{ }

1.3 C++11中的std::initializer_list

总结一下:

2、lambda

2.1 lambda的语法

2.2 捕捉列表

2.3 lambda的应用

2.4 lambda的原理

3、包装器

3.1 function

3.2 bind


1、{ }初始化

1.1 C++98的{ }

C++98中一般数组结构体可以用{ }进行初始化

struct Point
{
    int _x;
    int _y;
};

int main()
{
    int array1[] = {1, 2, 3, 4, 5};
    int array2[5] = {0};
    Point p = {1, 2};
    
    return 0;
}

1.2 C++11的{ }

C++11 以后想统一初始化方式,试图实现一切对象皆可用 { } 初始化{ } 初始化也叫做列表初始化

内置类型支持自定义类型支持自定义类型本质是类型转换,中间会产生临时对象,最后优化
了以后变成直接构造

{ } 初始化的过程中,可以省略 

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

struct Point
{
    int _x;
    int _y;
};

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {
        cout << "Date(int year, int month, int day)" << endl;
    }

    Date(const Date& d)
        : _year(d._year)
        , _month(d._month)
        , _day(d._day)
    {
        cout << "Date(const Date& d)" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    // C++98 支持的初始化方式
    int a1[] = {1, 2, 3, 4, 5};
    int a2[5] = {0};
    Point p = {1, 2};

    // C++11 支持的初始化方式
    // 内置类型支持
    int x1 = {2};

    // 自定义类型支持
    // 这里本质是用 {2025, 1, 1} 构造一个 Date 临时对象
    // 临时对象再去拷贝构造 d1,编译器优化后合二为一变成 {2025, 1, 1} 直接构造初始化 d1
    // 运行一下,我们可以验证上面的理论,发现是没调用拷贝构造的
    Date d1 = {2025, 1, 1};

    // 这里 d2 引用的是 {2024, 7, 25} 构造的临时对象
    const Date& d2 = {2024, 7, 25};

    // 需要注意的是 C++98 支持单参数时类型转换,也可以不用 {}
    Date d3 = {2025};
    Date d4 = 2025;

    // 可以省略掉 =
    Point p1{1, 2};
    int x2{2};
    Date d6{2024, 7, 25};
    const Date& d7{2024, 7, 25};

    // 不支持,只有 {} 初始化,才能省略 =
    // Date d8 2025;

    vector<Date> v;
    v.push_back(d1);
    v.push_back(Date(2025, 1, 1));

    // 比起有名对象和匿名对象传参,这里 {} 更有性价比
    v.push_back({2025, 1, 1});

    return 0;
}

1.3 C++11中的std::initializer_list

但是对象容器初始化还是不太方便,比如一个vector对象,我想用N个值构造初始化,那么我们得实现很多个构造函数才能支持

vector<int> v1 = {1, 2, 3};
vector<int> v2 = {1, 2, 3, 4, 5};

C++11 库中提出了一个 std::initializer_list 的类,

auto il = { 10, 20, 30 };  // the type of il is an initializer_list

这个类的本质是底层开一个数组,将数据拷贝过来std::initializer_list 内部有两个指针分别指向数组的开始结束支持迭代器遍历

这是它的文档:initializer_list - C++ Reference 。

容器支持一个 std::initializer_list构造函数,也就支持任意多个值构成的初始化。STL 中的容器支持用 {x1, x2, x3...} 进行初始化,就是通过 std::initializer_list 的构造函数支持的。

// 标准库中的声明示例

// vector 的 initializer_list 构造函数
vector(initializer_list<value_type> il, 
       const allocator_type& alloc = allocator_type());

// list 的 initializer_list 构造函数
list(initializer_list<value_type> il, 
     const allocator_type& alloc = allocator_type());

// map 的 initializer_list 构造函数
map(initializer_list<value_type> il,
    const key_compare& comp = key_compare(),
    const allocator_type& alloc = allocator_type());

// 自定义 vector 的简化实现
template<class T>
class vector {
public:
    typedef T* iterator;  // 迭代器类型

    // initializer_list 构造函数
    vector(initializer_list<T> il) {
        for (auto& e : il) {  // 遍历列表元素
            push_back(e);     // 逐个插入容器
        }
    }

private:
    iterator _start = nullptr;        // 指向首元素
    iterator _finish = nullptr;       // 指向最后一个元素的下一个位置
    iterator _endofstorage = nullptr; // 指向存储空间的末尾
};

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main() {
    // 1. 测试 initializer_list 的基本特性
    std::initializer_list<int> mylist;
    mylist = {10, 20, 30};  // 初始化列表赋值
    
    // 输出 initializer_list 的大小(通常是固定大小,包含两个指针)
    cout << "sizeof(mylist): " << sizeof(mylist) << endl;

    // 2. 验证 initializer_list 的存储位置
    int i = 0;
    cout << "mylist.begin(): " << mylist.begin() << endl;  // 指向第一个元素
    cout << "mylist.end(): " << mylist.end() << endl;      // 指向末尾后一位
    cout << "&i: " << &i << endl;                          // 对比栈地址

    // 3. 容器的两种初始化方式对比
    vector<int> v1({1, 2, 3, 4, 5});       // 直接构造(显式 initializer_list)
    vector<int> v2 = {1, 2, 3, 4, 5};      // 隐式转换(可能触发拷贝优化)
    const vector<int>& v3 = {1, 2, 3, 4, 5}; // 引用临时对象(生命周期延长)

    // 4. map 的嵌套初始化(pair + initializer_list)
    map<string, string> dict = {
        {"sort", "排序"}, 
        {"string", "字符串"}
    };

    // 5. initializer_list 赋值操作
    v1 = {10, 20, 30, 40, 50};  // 调用 operator=(initializer_list)

    return 0;
}

总结一下:

C++11,基本实现了一切对象皆可初始化。 

2、lambda

2.1 lambda的语法

lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是它可以定义在函数内部
lambda 表达式语法层面而言没有类型,所以我们一般是用auto或者模板参数定义的对象去接收。

lambda 表达式格式

[capture-list] (parameters) -> return type { function body }

[capture-list]捕捉列表编译器根据 [ ] 判断是否为 lambda 函数捕捉列表能够捕捉上下文中的变量供 lambda 函数使用,捕捉列表可以传值传引用捕捉,具体细节在 2.2 中我们再细讲。捕捉列表为空 [ ] 也不能省略

(parameters)参数列表,与普通函数的参数列表功能类似,如果不需要参数传递,则可以连同( )一起省略

-> return type返回值类型,用追踪返回类型形式(允许将函数的返回类型放在参数列表之后(用->引导))声明函数的返回值类型没有返回值时此部分可省略。一般返回值类型明确情况下,也可省略,由编译器对返回类型进行推导

{ function body }函数体,函数体内的实现跟普通函数完全类似,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量函数体为空{ }也不能省略

#include <iostream>
using namespace std;

int main() {
    // 1. 一个简单的lambda表达式
    auto add1 = [](int x, int y)->int { return x + y; };
    cout << add1(1, 2) << endl;  // 输出: 3

    /* Lambda表达式特性说明:
       1. 捕捉列表为空也不能省略(必须写[])
       2. 参数列表为空可以省略(如func1示例)
       3. 返回值可以省略,可以通过返回对象自动推导
       4. 函数体不能省略
    */

    // 2. 无参数、无显式返回类型的lambda
    auto func1 = [] {
        cout << "hello bit" << endl;
        return 0;
    };
    func1();  // 输出: hello bit

    // 3. 通过引用修改外部变量
    int a = 0, b = 1;        
    auto swap1 = [](int& x, int& y) {
        int tmp = x;
        x = y;
        y = tmp;
    };
    swap1(a, b);
    cout << a << ":" << b << endl;  // 输出: 1:0

    return 0;
}

2.2 捕捉列表

Lambda 表达式的变量捕捉规则

在 lambda 表达式中,默认只能使用 lambda 函数体和参数中的变量。如果想使用外层作用域中的变量,就需要进行捕捉。

第一种捕捉方式:显式捕捉

在捕捉列表中显式指定 传值捕捉 和 传引用捕捉,多个变量用逗号分隔。

[x, y, &z] 表示 x y 值捕捉z 引用捕捉

第二种捕捉方式:隐式捕捉

[=] 表示 隐式值捕捉[&] 表示 隐式引用捕捉

lambda 表达式中使用了哪些变量,编译器就会自动捕捉哪些变量。即按需捕捉

第三种捕捉方式:混合捕捉

在捕捉列表中 混合使用隐式捕捉和显式捕捉

[=, &x] 表示其他变量 隐式值捕捉引用捕捉

[&, x, y] 表示其他变量 隐式引用捕捉x y 值捕捉

混合捕捉规则:

  • 第一个元素必须是 & =

  • & 混合捕捉时,后面的捕捉变量必须是 值捕捉

  • 混合捕捉时,后面的捕捉变量必须是 引用捕捉


Lambda 表达式的变量作用域规则

  • 如果 lambda 表达式定义在函数局部作用域,它可以捕捉 lambda 位置之前定义的局部变量,但不能捕捉 静态局部变量 和 全局变量(它们可以直接使用,无需捕捉)。

  • 如果 lambda 表达式定义在全局位置,捕捉列表必须为空(不能捕捉任何变量)一般也不会定义在全局。


Lambda 的 const 性和 mutable 修饰

  • 默认情况下,传值捕捉是被 const  修饰的,即 传值捕捉的变量不能修改引用捕捉没有const修饰

  • 使用 mutable 修饰符(加在参数列表后面)可以取消 const 限制,使 传值捕捉的变量可以修改(但修改的是形参不影响实参)。

  • 使用 mutable 后,参数列表()不可省略(即使参数为空)

#include <iostream>
using namespace std;

int x = 0; // 全局变量

// 全局lambda的捕捉列表必须为空(全局变量可直接使用,无需捕捉)
auto func_global = []() {
    x++; // 直接修改全局变量
};

int main() {
    // 局部变量
    int a = 0, b = 1, c = 2, d = 3;

    // 1. 显式捕捉:值捕捉a,引用捕捉b
    auto func1 = [a, &b] {
        // a++;  // 错误:值捕捉的变量默认const,不可修改
        b++;    // 正确:引用捕捉可修改
        return a + b;
    };
    cout << func1() << endl;

    // 2. 隐式值捕捉(=):自动捕捉所有使用的局部变量(a,b,c)
    auto func2 = [=] {
        return a + b + c; // d未使用,不会被捕捉
    };
    cout << func2() << endl;

    // 3. 隐式引用捕捉(&):自动引用捕捉所有修改的变量
    auto func3 = [&] {
        a++; c++; d++; // 全部通过引用修改
    };
    func3();
    cout << a << " " << b << " " << c << " " << d << endl;

    // 4. 混合捕捉1:默认引用捕捉,显式值捕捉a,b
    auto func4 = [&, a, b] {
        // a++; b++;  // 错误:a,b是值捕捉
        c++; d++;     // 正确:其他变量隐式引用捕捉
        return a + b + c + d;
    };
    func4();
    cout << a << " " << b << " " << c << " " << d << endl;

    // 5. 混合捕捉2:默认值捕捉,显式引用捕捉a,b
    auto func5 = [=, &a, &b] {
        a++; b++;     // 正确:a,b是引用捕捉
        // c++; d++;  // 错误:其他变量隐式值捕捉
        return a + b + c + d;
    };
    func5();
    cout << a << " " << b << " " << c << " " << d << endl;

    // 6. 静态/全局变量无需捕捉
    static int m = 0;
    auto func6 = [] {
        return x + m; // 直接使用全局和静态变量
    };

    // 7. mutable修饰:允许修改值捕捉的副本(不影响外部变量)
    auto func7 = [=]() mutable {
        a++; b++; c++; d++; // 修改的是内部副本
        return a + b + c + d;
    };
    cout << func7() << endl;   // 输出副本修改后的值
    cout << a << " " << b << " " << c << " " << d << endl; // 原变量不变

    return 0;
}

2.3 lambda的应用

在学习 lambda 表达式之前,我们使用的可调用对象只有函数指针仿函数对象函数指针
类型定义起来比较麻烦仿函数定义一个类,相对会比较麻烦。使用 lambda 去定义可调用对象,既简单方便

lambda 在很多其他地方用起来也很好用。比如线程中定义线程的执行函数逻辑,智能指针中定
制删除器等,lambda 的应用还是很广泛的,以后我们会不断接触到。

struct Goods
{
    string _name;  // 名字
    double _price; // 价格
    int _evaluate; // 评价

    // ...
    Goods(const char* str, double price, int evaluate)
        :_name(str)
        , _price(price)
        , _evaluate(evaluate)
    {}
};

struct ComparePriceLess
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price < gr._price;
    }
};

struct ComparePriceGreater
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price > gr._price;
    }
};

int main()
{
    vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3 }, { "菠萝", 1.5, 4 } };

    // 类似这样的场景,我们实现仿函数对象或者函数指针支持商品中
    // 不同项的⽐较,相对还是⽐较⿇烦的,那么这⾥ lambda 就很好⽤了

    sort(v.begin(), v.end(), ComparePriceLess());
    sort(v.begin(), v.end(), ComparePriceGreater());

    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._price < g2._price;
    });

    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._price > g2._price; 
    });

    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._evaluate < g2._evaluate;
    });

    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._evaluate > g2._evaluate;
    });

    return 0;
}

2.4 lambda的原理

lambda原理范围 for 相似,编译后从汇编指令层的角度看,压根就没有 Lambda 和范围 for 这样的东西。范围 for 底层是迭代器,而 lambda 底层是仿函数对象,也就是说我们写了一个 lambda 以后,编译器会生成一个对应的仿函数的类

仿函数的类名编译器按一定规则生成的保证不同的 lambda 生成的类名不同

lambda 参数 / 返回类型 / 函数体就是仿函数 operator () 的参数 / 返回类型 / 函数体

lambda捕捉列表本质是初始化 仿函数类的成员变量,也就是说捕捉列表的变量都是 lambda 类构造函数的实参,当然隐式捕捉时,编译器要看使用哪些就传哪些对象

上面的原理,我们可以透过汇编层了解一下,下面第二段汇编层代码印证了上面的原理。

class Rate
{
public:
    Rate(double rate) 
        : _rate(rate)
    {}

    double operator()(double money, int year)
    {
        return money * _rate * year;
    }

private:
    double _rate;
};

int main()
{
    double rate = 0.49;

    // lambda 表达式
    auto r2 = [rate](double money, int year) {
        return money * rate * year;
    };

    // 函数对象(仿函数)
    Rate r1(rate);
    r1(10000, 2);  // 调用仿函数
    r2(10000, 2);  // 调用 lambda

    // 无参 lambda
    auto func1 = [] {
        cout << "hello world" << endl;
    };
    func1();  // 调用无参 lambda

    return 0;
}
// lambda 表达式
auto r2 = [rate](double money, int year) {
    return money * rate * year;
};

// 捕捉列表的 `rate`,可以看到作为 `lambda_1` 类构造函数的参数传递了,
// 这样在初始化成员变量后,才能在下面的 operator() 中使用
// 
// 汇编代码片段:
// 00D8295C  lea         eax,[rate]  
// 00D8295F  push        eax  
// 00D82960  lea         ecx,[r2]  
// 00D82963  call        `main'::`2'::<lambda_1>::<lambda_1> (0D81F80h)  

// 函数对象(仿函数)
Rate r1(rate);
// 汇编代码片段:
// 00D82968  sub         esp,8  
// 00D8296B  movsd       xmm0,mmword ptr [rate]  
// 00D82970  movsd       mmword ptr [esp],xmm0  
// 00D82975  lea         ecx,[r1]  
// 00D82978  call        Rate::Rate (0D81438h)  

r1(10000, 2);
// 汇编代码片段:
// 00D8297D  push        2  
// 00D8297F  sub         esp,8  
// 00D82982  movsd       xmm0,mmword ptr [__real@40c3880000000000 (0D89B50h)]  
// 00D8298A  movsd       mmword ptr [esp],xmm0  
// 00D8298F  lea         ecx,[r1]  
// 00D82992  call        Rate::operator() (0D81212h)  

// 从汇编层可以看到,r2(lambda 对象)的调用本质还是调用 operator(),
// 其类型是 `lambda_1`,这个类型名的规则由编译器自定义,确保不同的 lambda 不冲突
r2(10000, 2);
// 汇编代码片段:
// 00D82999  push        2  
// 00D8299B  sub         esp,8  
// 00D8299E  movsd       xmm0,mmword ptr [__real@40c3880000000000 (0D89B50h)]  
// 00D829A6  movsd       mmword ptr [esp],xmm0  
// 00D829AB  lea         ecx,[r2]  
// 00D829AE  call        `main'::`2'::<lambda_1>::operator() (0D824C0h)  

3、包装器

3.1 function

1. std::function 是一个类模板,也是一个包装器
2. std::function 实例对象可以包装存储其他的可调用对象,包括函数指针仿函数lambda 等,统一类型,存储的可调用对象被称为 std::function 的目标。若 std::function 不含目标,则称它为空。调用空 std::function 的目标会导致抛出 std::bad_function_call 异常。

// 前置声明(未定义的通用模板)
template <class T>
class function;  // undefined base template

// 特化版本:支持函数类型签名(返回类型 + 参数列表)
template <class Ret, class... Args>
class function<Ret(Args...)> {
    // 实现内容(此处未展开)
    // 通常包含 operator() 以支持函数调用
};

以上是 std::function 的原型,它被定义在 <functional> 头文件中。

官方文档:std::function - cppreference.com

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

// 普通函数
int f(int a, int b) {
    return a + b;
}

// 仿函数(函数对象)
struct Functor {
    int operator()(int a, int b) {
        return a + b;
    }
};

// 类(包含静态和非静态成员函数)
class Plus {
public:
    Plus(int n = 10) : _n(n) {}

    // 静态成员函数
    static int plusi(int a, int b) {
        return a + b;
    }

    // 非静态成员函数(依赖 this 指针)
    double plusd(double a, double b) {
        return (a + b) * _n;
    }

private:
    int _n;
};

int main() {
    // 1. 包装普通函数
    function<int(int, int)> f1 = f;
    cout << f1(1, 1) << endl;  // 输出: 2

    // 2. 包装仿函数对象
    function<int(int, int)> f2 = Functor();
    cout << f2(1, 1) << endl;  // 输出: 2

    // 3. 包装 lambda 表达式
    function<int(int, int)> f3 = [](int a, int b) { return a + b; };
    cout << f3(1, 1) << endl;  // 输出: 2

    // 4. 包装静态成员函数(无需对象实例)
    function<int(int, int)> f4 = &Plus::plusi;
    cout << f4(1, 1) << endl;  // 输出: 2

    // 5. 包装非静态成员函数(需绑定对象)
    Plus pd(10);  // _n = 10

    // 方式1:传递对象指针
    function<double(Plus*, double, double)> f5 = &Plus::plusd;
    cout << f5(&pd, 1.1, 1.1) << endl;  // 输出: (1.1 + 1.1) * 10 = 22

    // 方式2:传递对象值(拷贝)
    function<double(Plus, double, double)> f6 = &Plus::plusd;
    cout << f6(pd, 1.1, 1.1) << endl;    // 输出: 22

    // 方式3:传递右值引用
    function<double(Plus&&, double, double)> f7 = &Plus::plusd;
    cout << f7(move(pd), 1.1, 1.1) << endl;  // 输出: 22
    cout << f7(Plus(10), 1.1, 1.1) << endl;  // 输出: 22(临时对象)

    return 0;
}

静态成员函数不属于对象,不需要传this指针,非静态成员函数需传递this指针,传递对象(会自行取对象地址)或对象地址。 

3.2 bind

1. 基本形式(自动推导返回类型)

template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);

2. 指定返回类型(C++14 起支持)

template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);

bind 是一个函数模板,它也是一个可调用对象的包装器,可以把它看做一个函数适配器封装函数/可调用对象),对接收 的 fn 可调用对象进行处理返回一个可调用对象

bind 可以用来调整参数个数和参数顺序。bind 也在<functional>这个头文件中。

调用 bind 的一般形式: auto newCallable = bind(callable,arg_list); 其中 newCallable 本身是一个可调用对象arg_list 是一个逗号分隔参数列表对应给定的 callable参数。当我们调用 newCallable 时,newCallable 会调用 callable,并传给它 arg_list 中的参数。

arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数,表示 newCallable参数。数值 n 表示生成的可调用对象中参数的位置:_1 newCallable第一个参数(无所谓_1的位置在哪)_2第二个参数,以此类推。_1/_2/_3.... 这些占位符 placeholders 的一个命名空间中。

#include <iostream>
#include <functional>

// 定义一个减法函数,返回 (a - b) * 10
int Sub(int a, int b) {
    return (a - b) * 10;
}

// 定义一个减法函数,返回 (a - b - c) * 10
int SubX(int a, int b, int c) {
    return (a - b - c) * 10;
}

// 定义一个 Plus 类,包含静态和非静态的加法函数
class Plus {
public:
    // 静态加法函数
    static int plusi(int a, int b) {
        return a + b;
    }

    // 非静态加法函数
    double plusd(double a, double b) {
        return a + b;
    }
};

int main() {
    // 使用 std::placeholders 命名空间中的占位符
    using namespace std::placeholders;

    // 绑定 Sub 函数,正常顺序传递参数
    auto sub1 = std::bind(Sub, _1, _2);
    std::cout << sub1(10, 5) << std::endl;

    // 调整参数顺序,交换 _1 和 _2 的位置
    auto sub2 = std::bind(Sub, _2, _1);
    std::cout << sub2(10, 5) << std::endl;

    // 调整参数个数,固定第一个参数为 100
    auto sub3 = std::bind(Sub, 100, _1);
    std::cout << sub3(5) << std::endl;

    // 调整参数个数,固定第二个参数为 100
    auto sub4 = std::bind(Sub, _1, 100);
    std::cout << sub4(5) << std::endl;

    // 分别绑死 SubX 函数的第 1、2、3 个参数
    auto sub5 = std::bind(SubX, 100, _1, _2);
    std::cout << sub5(5, 1) << std::endl;

    auto sub6 = std::bind(SubX, _1, 100, _2);
    std::cout << sub6(5, 1) << std::endl;

    auto sub7 = std::bind(SubX, _1, _2, 100);
    std::cout << sub7(5, 1) << std::endl;

    // 绑定 Plus 类的非静态成员函数
    std::function<double(Plus&&, double, double)> f6 = &Plus::plusd;
    Plus pd;
    std::cout << f6(std::move(pd), 1.1, 1.1) << std::endl;
    std::cout << f6(Plus(), 1.1, 1.1) << std::endl;

    // 使用 std::bind 绑定 Plus 类的非静态成员函数,固定对象
    std::function<double(double, double)> f7 = 
    std::bind(&Plus::plusd, Plus(), _1, _2);
    std::cout << f7(1.1, 1.1) << std::endl;

    // 定义一个计算复利的 lambda 函数
    auto func1 = [](double rate, double money, int year) -> double {
        double ret = money;
        for (int i = 0; i < year; i++) {
            ret += ret * rate;
        }
        return ret - money;
    };

    // 绑死一些参数,实现支持不同年利率、不同金额和不同年份计算复利的结算利息
    std::function<double(double)> func3_1_5 = std::bind(func1, 0.015, _1, 3);
    std::function<double(double)> func5_1_5 = std::bind(func1, 0.015, _1, 5);
    std::function<double(double)> func10_2_5 = std::bind(func1, 0.025, _1, 10);
    std::function<double(double)> func20_3_5 = std::bind(func1, 0.035, _1, 30);

    std::cout << func3_1_5(1000000) << std::endl;
    std::cout << func5_1_5(1000000) << std::endl;
    std::cout << func10_2_5(1000000) << std::endl;
    std::cout << func20_3_5(1000000) << std::endl;

    return 0;
}    

相关文章:

  • Flutter常用组件实践
  • python+requests接口自动化测试框架实例教程
  • C#容器源码分析 --- Queue<T>
  • 2025届蓝桥杯JavaB组个人题解(暂时不全,没题目)
  • 【AI】AI大模型发展史:从理论探索到技术爆发
  • [创业之路-366]:投资尽职调查 - 尽调核心逻辑与核心影响因素:价值、估值、退出、风险、策略
  • webpack vite
  • 基于 Termux 在移动端配置 Ubuntu 系统并搭建工作环境
  • DeepSeek在应急救援领域的应用解决方案
  • docker测试镜像源
  • 如何在运行时获取硬件信息
  • day24 学习笔记
  • Linux:35.其他IPC和IPC原理+信号量入门
  • 自动驾驶的数据集以及yolov8和yolop
  • Oracle 复制表结构(含索引、主键)操作指南
  • 池式结构---内存池
  • 企业年报问答RAG挑战赛冠军方案:从零到SotA,一战封神
  • AI 大语言模型 (LLM) 平台的整体概览与未来发展
  • #关于数据库中的时间存储
  • 006.Gitlab CICD流水线触发
  • 网站建设与管理任务分工/百度广告平台
  • 玻璃制品东莞网站建设/郑州百度关键词seo
  • 中国十大咨询机构/seo站
  • 网站 中国最早做网站的/什么样的人适合做策划
  • 网站备案信息查询/如何进行关键词优化工作
  • 做soho 怎么建立网站/个人网页设计制作网站模板