C++11 新特性

1. 初始化

1.1 花括号 {} 列表初始化

复制代码
struct Point
{
    int _x;
    int _y;
};
​
class Date
{
public:
    Date(int year, int month, int day)
        :_year(year)
        , _month(month)
        , _day(day)
    {
        cout << "Date(int year, int month, int day)" << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
​
void test_1()
{
    // C++98支持花括号{ }对数组和结构体元素进行列表初始化
    int array1[] = { 1, 2, 3, 4, 5 };
    int array2[5] = { 0 };
    // Point p = { 1, 2 };
​
    // C++11扩大适用范围,支持内置类型和自定义类型,使用初始化列表时,‘=’ 可加可不加
    int x1 = 1;
    int x2{ 2 };
    int array3[]{ 1, 2, 3, 4, 5 };
    int array4[5]{ 0 };
    Point p{ 1, 2 };
​
    int* pa = new int[4] {0};   // 同样可以用于new表达式
​
    // C++11支持的列表初始化,这里会调用构造函数初始化
    Date d1{ 2022, 1, 2 };
    Date d2 = { 2022, 1, 3 };
}
  • C++98 中,花括号只能用于数组和 POD 结构体的聚合初始化。

  • C++11 将其扩展为通用列表初始化 ,内置类型、自定义类型均可使用,= 可省略。

  • 对于类类型,{...} 会调用对应的构造函数进行初始化。

  • new int[4]{0} 等动态分配也支持该语法,能有效防止未初始化值。


1.2 std::initializer_list

复制代码
void test_2()
{
    auto il = { 10, 20, 30 };
    // 当使用赋值形式的列表初始化 auto var = { ... } 且花括号内元素类型一致时,
    // auto 会推导为 std::initializer_list<T>
    cout << typeid(il).name() << endl;   // class std::initializer_list<int>
​
    // 标准容器都有接受 initializer_list 的构造函数
    vector<int> v = { 1, 2, 3 };
    map<std::string, int> m = { {"a", 1}, {"b", 2} };
}
  • std::initializer_list<T> 是 C++11 引入的轻量级容器,专门用于接收花括号列表。

  • auto{...} 的推导规则:若所有元素类型相同,推导为 initializer_list<T>;否则编译错误(C++17 略有变化)。

  • 标准库容器均提供接受 initializer_list 的构造函数,这使得可以直接用 {1,2,3}{"a",1}, {"b",2} 初始化容器。


2. 声明与类型推导:decltype

复制代码
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
    //decltype推导出对象的类型,可用于再定义或模板实参
    decltype(t1 * t2) ret;             // 推导表达式结果的类型
    cout << typeid(ret).name() << endl;
}
​
void test_3()
{
    int i = 10;
    auto p = &i;                       // auto 推导为 int*
    auto p1 = p;
    decltype(p) p2;                    // decltype 推导 p 的类型为 int*
    cout << typeid(p).name() << " " << typeid(p1).name() << " " << typeid(p2).name() << endl;
    
    F(1, 'a');                         // 输出 int
}
  • auto 根据初始化表达式推导变量类型decltype(exp) 则仅推导表达式本身的类型而不求值。

  • decltype 常用于模板编程中获取未知表达式的返回类型。

  • 本例中 F(1, 'a') 内部 t1 * t2 的结果类型为 int(char 被提升为 int),所以 retint


3. 新 STL 容器

3.1 std::arraystd::forward_list

复制代码
void test_4()
{
    // array 固定大小数组
    array<int, 4> arr{ 1, 2, 3, 4 };
    arr[0] = 10;                    // 不检查边界
    arr.at(1) = 20;                 // 检查边界,越界抛 std::out_of_range
​
    // forward_list 轻量化单向链表
    forward_list<int> fl{ 1, 2, 3 };
    fl.push_front(0);               // 头部插入
    auto it = fl.begin();
    fl.insert_after(it, 42);        // 在 it 之后插入
​
    for (int x : fl) std::cout << x << ' ';   // 0 42 1 2 3
    fl.erase_after(fl.begin());     // 删除 0 后面的元素,变为 0 1 2 3
}

讲解

  • std::array 是一个封装了固定大小数组的容器,支持迭代器和 at() 安全访问,零开销替代 C 风格数组。

  • std::forward_list 是单向链表,只支持从头或已知位置之后 插入/删除,没有 size() 方法,内存开销极小。所有操作需要前驱迭代器。


3.2 unordered_setunordered_map

复制代码
void test_5()
{
    //unordered_set 无序集合
    //去重、快速判断元素是否存在
    unordered_set<int> set{ 1, 2, 3, 4, 5 };
    set.insert(6);
    set.erase(2);
    if (set.find(3) != set.end())
        cout << "found 3\n";
    for (int x : set)
        cout << x << ' ';           // 无序输出
    cout << set.size() << endl;
    
    //unordered_map 无序映射
    unordered_map<std::string, int> map{ {"apple", 5}, {"banana", 3} };
    map["orange"] = 7;              // 插入或修改
    map["banana"] = 4;
    map.at("apple") = 10;           // 修改,不存在则抛异常
    auto it = map.find("banana");
    if (it != map.end())
        cout << it->first << " -> " << it->second << endl;
    map.erase("apple");
​
    for (const auto& [key, value] : map)   // 结构化绑定遍历(C++17)
        cout << key << ": " << value << endl;
}
  • unordered_set/map 基于哈希表,查找、插入、删除平均 O(1),元素无序。

  • 支持 initializer_list 初始化,支持 []at()finderase 等操作。

  • 使用 find[] 更安全(不会意外插入不存在的键)。

  • C++17 结构化绑定 [key, value] 可优雅地遍历键值对。


3.3 新接口 emplace 系列

复制代码
//emplace_back 利用 可变参数模板 + 完美转发,全过程只有一次构造,没有临时对象,也不要求对象可拷贝或可移动。
template <typename... Args>
void emplace_back(Args&&... args)
{
    // 在容器预留的内存上直接调用 T 的构造函数
    // new (_mem) T(std::forward<Args>(args)...);
}

讲解

  • emplace_back / emplace 利用可变参数模板 + 完美转发,在容器内存地址上直接构造对象,避免临时对象创建和拷贝/移动。

  • 例如 vector<MyString> v; v.emplace_back("hello") 只调用一次 MyString(const char*) 构造,效率高于 push_back


4. 右值引用与移动语义(重点)

4.1 左值与左值引用

复制代码
void test_6()
{
    int* p = new int(0);
    int b = 1;
    const int c = 2;
​
    int*& rp = p;
    int& rb = b;
    const int& rc = c;
    int& pvalue = *p;
}
  • 左值 :可以取地址、有名字的表达式(如变量 b、解引用 *p)。const 左值虽然不能赋值,但仍可取其地址,属于左值。

  • 左值引用 (T&):绑定到左值的别名,可修改原对象。

  • const 左值引用 (const T&) :既可以绑定左值,也可以绑定右值,是"万能引用"(注意与万能转发引用 T&& 区分)。


4.2 右值与右值引用

复制代码
int fmin(int a, int b) { return a < b ? a : b; }
​
void test_7()
{
    double x = 1.1, y = 2.2;
    10;               // 字面常量,右值
    // x + y;        // 表达式返回值,右值
    fmin(x, y);      // 函数返回值(非引用返回),右值
​
    // 右值不能作为左操作数
    // 10 = 1;        // 错误
    // x + y = 1;     // 错误
    "hello world";//常量字符串本身是右值,但是"xxx"表示字符串首地址
    
    int&& rr1 = 10;
    double&& rr2 = x + y;
    double&& rr3 = fmin(x, y);
​
    const int& lv = 10;              // const 左值引用可绑定右值
    const double& lv2 = x + y;
​
    int a = 10;
    int&& ax = move(a);              //右值引用左值需要move 将左值强行转为右值
}

讲解

  • 右值:不能取地址的临时对象,如字面量、表达式结果、非引用返回的函数结果。

  • 右值引用 (T&&):专门绑定右值的引用,延长临时对象生命周期,并可"窃取"其资源实现移动语义。

  • std::move():本身不做任何移动,只是将左值无条件转换为右值引用,使其能被移动构造函数或移动赋值函数匹配。


4.3 移动构造与移动赋值

复制代码
class MyString
{
    char* _data;
public:
    MyString(const char* s = "") 
    {
        _data = new char[strlen(s) + 1];
        strcpy(_data, s);
        cout << "[构造] " << _data << endl;
    }
    // 拷贝构造(深拷贝)
    MyString(const MyString& other) 
    {
        _data = new char[strlen(other._data) + 1];
        strcpy(_data, other._data);
        cout << "[拷贝构造] " << _data << endl;
    }
    // 移动构造(窃取资源)
    MyString(MyString&& other) noexcept : _data(other._data) 
    {
        other._data = nullptr;
        cout << "[移动构造] " << (_data ? _data : "null") << endl;
    }
    // 拷贝赋值
    MyString& operator=(const MyString& other) 
    {
        if (this != &other) {
            delete[] _data;
            _data = new char[strlen(other._data) + 1];
            strcpy(_data, other._data);
            cout << "[拷贝赋值] " << _data << endl;
        }
        return *this;
    }
    // 移动赋值
    MyString& operator=(MyString&& other) noexcept 
    {
        if (this != &other) {
            delete[] _data;
            _data = other._data;
            other._data = nullptr;
            cout << "[移动赋值] " << (_data ? _data : "null") << endl;
        }
        return *this;
    }
    ~MyString() { delete[] _data; }
};
​
MyString getString() 
{
    MyString str("hello world");
    return str;          // 编译器将局部对象str视为右值,触发移动
}
​
void test_8() 
{
    cout << "--- test_8 start ---" << endl;
    MyString s = getString();        // ① 移动构造
    cout << "next" << endl;
    MyString s2;
    s2 = getString();                // ② 移动赋值
    cout << "s = " << s << ", s2 = " << s2 << endl;
    cout << "--- test_8 end ---" << endl;
}
  • 移动构造函数:将源对象的资源指针直接"窃取"到新对象,并将源对象指针置空,避免深拷贝的开销。

  • 移动赋值运算符:先释放自身资源,再窃取源对象资源,同样将源对象置空。

  • 当函数返回局部对象时,编译器会自动将其视为右值,优先匹配移动构造。

  • noexcept 对于移动操作很重要,它允许标准库容器在扩容时选择更高效的移动而非拷贝。


4.4 左值引用缺陷与右值引用价值

复制代码
void process(const MyString& s) 
{
    cout << "process(const&): 只能读,不能移动: " << s << endl;
    MyString copy = s;               // 必须拷贝
}
​
void process(MyString&& s) 
{
    cout << "process(&&): 可以移动资源: " << s << endl;
    MyString moved = std::move(s);   // 移动构造,s 变空
}
​
void test_9() 
{
    test_8();
    cout << "\n=== 演示左值引用缺陷和右值引用优势 ===\n" << endl;
​
    MyString a("I am an lvalue");
    const MyString& ref = a;
​
    // 缺陷1:无法区分左/右值,只能提供一种按引用传递的方式
    cout << "调用 process(const&) 传入左值: ";
    process(a);                          // const& 版本,内部只能拷贝
    cout << "调用 process(const&) 传入右值: ";
    process(MyString("temporary"));      // 仍是 const&,无法移动资源
​
    // 右值引用意义:通过重载,右值自动匹配移动版本
    cout << "调用 process(&&) 传入右值: ";
    process(MyString("temporary"));      // 调用 && 版本,移动构造
}
  • 仅用 const T& 无法区分实参是左值还是右值,函数内部一律只能进行拷贝,即使传入的是即将销毁的临时对象也无法"窃取"其资源。

  • 通过重载 T&& 版本,编译器会在实参为右值时自动选择移动版本,实现零拷贝的资源转移,显著提升性能。

  • 移动语义本质:将"拷贝"替换为"资源转移",适用于持有堆内存、文件句柄等昂贵资源的对象。


5. 完美转发

在 C++ 中,当你写一个模板函数,它接收的参数需要原封不动地传递给另一个函数时,我们希望:

1.如果传入的是左值,目标函数收到的也是左值(能够匹配 const T& 或 T& 版本);

2.如果传入的是右值,目标函数收到的也是右值(能够匹配 T&& 版本,从而触发移动语义)。

完美转发就是通过 万能引用(T&&) 和 std::forward 来实现这种**"参数值类别不变"**的传递。

5.1 原理与实现

复制代码
template<typename T>
void perfect_wrapper(T&& arg) // 万能引用
{      
    process(std::forward<T>(arg));   // 保持原始值类别
    //当传入左值 MyString a; 时,T 被推导为 MyString&,std::forward<T>(arg) 返回左值引用。
    //当传入右值 MyString("temp") 时,T 被推导为 MyString,std::forward<T>(arg) 返回右值引用。
}
​
template<typename T>
void badWrapper(T&& arg) 
{
    cout << "[badWrapper] 准备转发(错误方式)..." << endl;
    process(arg);   // 没有 forward
}
​
void test_10() 
{
    cout << "\n========== test_10 完美转发演示 ==========" << endl;
​
    MyString a("I am lvalue");
    cout << "\n--- Pass lvalue (a) ---" << endl;
    perfectForwardWrapper(a);
​
    cout << "\n--- Pass rvalue (temporary) ---" << endl;
    perfectForwardWrapper(MyString("temporary rvalue"));
​
    cout << "\n--- Compare: wrong version without forward ---" << endl;
    cout << "Pass rvalue but badWrapper uses arg directly:" << endl;
    badWrapper(MyString("will be passed wrongly"));
​
    cout << "\n--- Verify move semantics triggered ---" << endl;
    MyString source("I have precious data");
    cout << "source = " << source << endl;
    perfectForwardWrapper(std::move(source));
    cout << "After move, source = " << source << " (empty)" << endl;
}
​
  • 万能引用template<typename T> void f(T&& arg) 中,T&& 不是单纯的右值引用。当传入左值时,T 推导为 T&,引用折叠使 T&& 变为 T&;传入右值时,T 推导为 T,保持右值引用。

  • std::forward<T>(arg):条件式地将参数转发,左值转发为左值,右值转发为右值,是实现完美转发的核心。

  • 为什么要完美转发 :模板函数中参数变为有名变量后一律为左值,直接传递会丢失右值属性,无法调用移动版本。forward 能"记住"参数的原始值类别并恢复。

5.2 错误做法对比

复制代码
template<typename T>
void badWrapper(T&& arg) 
{
    cout << "[badWrapper] 准备转发(错误方式)..." << endl;
    process(arg);   // arg 是左值,永远调用 process(const&)
}
​
template<typename T>
void bad_wrapper(T& arg) 
{      // 只能接受左值
    process(arg);
}
​
template<typename T>
void bad_wrapper(T&& arg) 
{     
    // 能接受右值,但 arg 在函数体内是左值(有名变量)
    process(arg);               // 还是调用 process(const&)!
}

说明 :如果不使用 std::forward,即使传入右值,函数体内 arg 仍被视为左值,导致只匹配非移动版本,完美转发失败。


6. Lambda 表达式

6.1 基本语法与使用

复制代码
[ capture ] ( params ) mutable exception -> ret { body }
​
[ capture ]:捕获列表,说明 lambda 体内可以访问哪些外部变量、以什么方式访问。不可省略(哪怕为空也写 [])。
​
(params):参数列表(同普通函数)。如果没有参数可写空(),C++11 中不能省略;C++14 起可省略(若无 mutable / exception / ret)。
​
mutable:可选,使 lambda 体内能修改按值捕获的副本,并允许调用非 const 成员函数。
​
exception:可选,异常说明(如 noexcept),几乎很少用。
​
->ret:尾置返回类型。大多数情况可由编译器自动推导,可省略。但如果 lambda 内包含多个return 且类型不同必须指明。
​
{ body }:函数体。
复制代码
struct Goods
{
    string _name;
    double _price;
    int _evaluate;
​
    Goods(const char* str, double price, int evaluate)
        :_name(str)
        ,_price(price)
        ,_evaluate(evaluate)
    {}
};
​
struct ComparePrice
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price < gr._price;
    }
};
//....
​
void test_11()
{
    vector<Goods> vg{ {"苹果",2.1,3},{"香蕉",3.2,4},{"草莓",3.3,5} };
    sort(vg.begin(), vg.end(), ComparePrice());
    //繁琐,需要不同Compare
​
    auto compare1 = [](int x, int y)->bool {return x > y; };
    cout << compare1(1, 2) << endl;
​
    auto CompareGoodsPrice = [](const Goods& x, const Goods& y){return x._price < y._price; };//返回值类型可以省略
    //cout << CompareGoodsPrice(vg[0], vg[1]) << endl;
    sort(vg.begin(), vg.end(), CompareGoodsPrice);
​
    sort(vg.begin(), vg.end(), [](const Goods& x, const Goods& y) {
        return x._evaluate < y._evaluate; });
}
  • Lambda 语法:[捕获](参数) mutable 异常 -> 返回类型 { 函数体 }

  • 返回类型可省略,由编译器自动推导。

  • 本质是生成一个匿名函数对象(闭包),可以像函数一样调用,也可以作为 sort 等算法的预测。

6.2 捕获列表

复制代码
//[]        不捕获任何变量[]{}
//[x]       按值捕获 x(在 lambda 创建时拷贝一份)[x]{ return x; }
//[&x]      按引用捕获 x[&x]{ x = 10; }
//[=]       按值捕获所有外围局部变量(默认行为)[=]{ return a + b; }
//[&]       按引用捕获所有外围局部变量[&]{ a = 1; }
//[this]    捕获当前类的 this 指针(可访问成员)[this]{ return this->x; }
复制代码
class Foo 
{
public:
    int member = 100;
    void demoThis() 
    {
        // [this] 捕获:lambda 体内可直接访问成员变量和成员函数
        auto f = [this]() 
        {
            cout << "member = " << member << endl; // 等价于 this->member
            member += 10; // 可以修改成员
        };
        f();
        cout << "after f, member = " << member << endl; //110
    }
};
​
void test_12()
{
    int a = 1, b = 2, c = 3;
​
    // ---- 1. [] 空捕获 ----
    auto f1 = []() {
        return 42;      // 只能使用 lambda 内部定义的东西
        };
    cout << "f1(): " << f1() << endl; // 42
​
    // ---- 2. [x] 按值捕获 ----
    auto f2 = [a]() {
        return a + 10; // a 是定义时拷贝的副本
        };
    a = 100;
    cout << "f2() (a=1 copied): " << f2() << endl; // 11
​
    // ---- 3. [&x] 按引用捕获 ----
    auto f3 = [&b]() {
        b = 200;       // 直接修改外部 b
        };
    f3();
    cout << "b after f3: " << b << endl; // 200
​
    // ---- 4. [=] 按值捕获所有外围局部变量 ----
    auto f4 = [=]() {
        // 可以读 a, b, c,但不能修改它们(默认 const)
        return a + b + c; // a=100, b=200, c=3  303
        };
    cout << "f4() sum: " << f4() << endl; // 303
​
    // ---- 5. [&] 按引用捕获所有外围局部变量 ----
    auto f5 = [&]() {
        a = 0;
        b = 0;
        c = 0;
        };
    f5();
    cout << "after f5: a=" << a << ", b=" << b << ", c=" << c << endl; // 全0
​
    // ---- 6. [this] 捕获当前类的 this 指针 ----
    Foo foo;
    foo.demoThis();  // 内部展示 this 捕获
​
    // ---- 补充:混合捕获 ----
    int x = 10, y = 20, z = 30;
    // 除 y 按引用外,其余变量全部按值捕获
    auto f6 = [=, &y]() mutable {
        x++;    // mutable允许修改的是 lambda 内部的拷贝副本.
        y = 999; // 修改外部的 y
        // z 可以读,但不能修改(值捕获副本即 const)
        return x + y + z; // x=11, y=999, z=30  1040
        };
    cout << "f6(): " << f6() << endl; // 1040
    cout << x << " " << y << " " << z << endl; // 10 999 30
​
}
  • []:不捕获;[x]:按值捕获;[&x]:按引用捕获;[=]:按值捕获所有自动变量;[&]:按引用捕获所有;[this]:捕获当前对象指针,可访问成员。

  • 按值捕获的变量在 lambda 内默认为 const,加上 mutable 可取消,但修改的只是副本,不影响外部。

  • 混合捕获如 [=, &y] 表示除 y 按引用外,其余全部按值。


7. 可变模板参数

C++11 允许模板接受任意数量、任意类型的参数,语法用 ...

复制代码
template<typename... Args>        // Args 叫“模板参数包”
void funcA(Args... args)// args 叫“函数参数包” 
{         
    //这里的 Args 可以匹配零个或多个类型,args 则对应零个或多个值。
    //例如:funcA(1, 2.5, "hello"); → Args 被推导为 int, double, const char* ,args包含这三个实参。
}
​
//不能直接遍历参数包,必须通过编译期 递归展开 或利用 列表初始化
// 递归终止函数:无参数时什么都不做
void Print() 
{
    cout << endl;
}
//1.递归展开
template<typename T, typename... Args>
void Print(T first, Args... rest) 
{
    cout << first << ' ';
    Print(rest...);   // 递归调用,参数少一个
}
​
//2.利用初始化列表的展开
template<typename... Args>
void Print_all(Args... args)
{
    int dummy[] = { (cout << args << ' ', 0)... };//(expression)... 是将表达式对参数包中的每个参数展开   其中(..., 0) 是为了让每个逗号表达式的结果为 0 以初始化数组。
    cout << endl;
    (void)dummy; // 避免未使用变量警告
}
​
void test_13()
{
    Print(1, 2.5, "hello", 'c'); // 输出:1 2.5 hello c
​
    Print_all(1, 2.5, "hello", 'c'); // 输出:1 2.5 hello c
​
}
  • typename... Args 为模板参数包,Args... args 为函数参数包。

  • 不能直接遍历参数包,需通过编译期递归列表初始化结合逗号表达式展开

  • 递归版本每次剥离一个参数,直到空参数递归终止。

  • 列表初始化展开利用 (expression)... 对参数包中的每个参数执行表达式,用逗号表达式确保返回 0 以初始化数组。


8. std::function 包装器与 std::bind 绑定器

8.1 std::function 统一可调用对象

function是一个通用的多态函数容器,定义在 <functional> 头文件中。它能存储、复制和调用任何可调用目标,只要目标与指定的函数签名兼容。

复制代码
//普通模板版本
template<class F, class T>
T useF1(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;
    }
};
​
//包装器版本
double useF2(const function<double(double)>& f, double x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}
​
double add(double a, double b) { return a + b; }
double divide(double a, double b) { return a / b; }
​
struct Computer 
{
    double value = 1.5;
    double compute(double x) const { return x * x + value; }
};
​
void test_14()
{
    //普通模板版本
     cout << "===== 模板版本 (useF1) =====" << endl;
    // 生成 useF<double(*)(double), double>
    cout << useF1(f, 11.11) << endl;
    // 生成 useF<Functor, double>
    cout << useF1(Functor(), 11.11) << endl;
    // 生成 useF<__lambda_123, double>
    cout << useF1([](double d)->double { return d / 4; }, 11.11) << endl;
    //此时useF被实例化成三份
​
    //function包装器版本
    cout << "===== 包装器版本 (useF2) =====" << endl;
    cout << useF2(f, 11.11) << endl;                     // f 被隐式包装
    cout << useF2(Functor(), 11.11) << endl;             // 仿函数临时对象被包装
    cout << useF2([](double d)->double { return d / 4; }, 11.11) << endl; // lambda 被包装
    //只生成一份 useF 函数体(所有可调用对象都统一通过 std::function 接口调用)
}
  • std::function<Ret(Args...)> 是一个多态函数包装器,可以存储函数指针、函数对象、lambda 等任何可调用实体。

  • 模板版本 useF1 会根据传入的可调用对象类型生成多个实例(代码膨胀),而 useF2 只生成一份,可调用对象通过类型擦除统一管理。

  • 极大提高了接口的灵活性和代码复用。

8.2 std::bind 参数绑定与占位符

bind 是 C++11 引入的参数绑定器,定义在 <functional>中,它可以将一个可调用对象的部分参数预先绑定固定值,或者调整参数顺序,从而生成一个新的可调用对象,与 std::function 协同工作,实现签名适配。

复制代码
double add(double a, double b) { return a + b; }
double divide(double a, double b) { return a / b; }
struct Computer {
    double value = 1.5;
    double compute(double x) const { return x * x + value; }
};
​
void test_14() 
{
    cout << "===== bind版本 =====" << endl;
    // 1. 绑定二元函数 add,固定第一个参数为 5.0
    cout << useF2(bind(add, 5.0, _1), 3.0) << endl;      // 8.0
​
    // 2. 绑定 divide,将被除数固定为 1.0,实现倒数
    cout << useF2(bind(divide, 1.0, _1), 4.0) << endl;   // 0.25
​
    // 3. 绑定成员函数
    Computer comp;
    auto memFn = bind(&Computer::compute, &comp,    _1);    // 将对象与 this 绑定
    cout << useF2(memFn, 3.0) << endl;                    // 3*3 + 1.5 = 10.5
​
    // 4. 甚至可通过 bind 重新排列参数顺序(演示翻转除法)
    auto flipped = bind(divide, _2, _1);                 // 二元函数,参数顺序调换
    // 但 useF2 只接受一元,这里体现不出来,可以单独调用:
    cout << "flipped(10, 2) = " << flipped(10, 2) << endl; // 2/10 = 0.2
​
    // 5. 也可以嵌套 bind(不常用,但可行)
    auto complicated = bind(add, bind(divide, _1, 2.0), _1); // (x/2) + x
    cout << useF2(complicated, 10.0) << endl;               // 10/2 + 10 = 15
}
  • std::bind 可以将函数的某些参数预先绑定为固定值,返回一个新的可调用对象,从而适配调用签名。

  • 占位符 _1, _2, ...(定义在 std::placeholders 命名空间)表示新函数的第几个参数。

  • 可以绑定成员函数,此时必须提供对象指针或引用作为第二个参数。

  • 支持参数重排、嵌套绑定等高级用法,结合 function 能灵活构建调用链。

相关推荐
我不是懒洋洋2 小时前
【数据结构】排序算法(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序)
c语言·数据结构·c++·经验分享·算法·排序算法
邪修king2 小时前
UE5:C++ 实现 游戏逻辑 ↔ UI 双向联动
c++·游戏·ue5
汉克老师11 小时前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
智者知已应修善业14 小时前
【51单片机2个按键控制流水灯运行与暂停】2023-9-6
c++·经验分享·笔记·算法·51单片机
云泽80816 小时前
C++11 核心特性全解:列表初始化、右值引用与移动语义实战
开发语言·c++
AI进化营-智能译站17 小时前
ROS2 C++开发系列12-用多态与虚函数构建可扩展的ROS2机器人行为模块
开发语言·c++·ai·机器人
Morwit17 小时前
QML组件之间的通信方案(暴露子组件)
c++·qt·职场和发展
qeen8717 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
图码17 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻