C++11初探:C++发展史上的里程碑

C++11 编译器在实现C++的时候为了形成逻辑的闭环,右值的右值引用可以被修改,属性退化。所以,我们在实现接口的时候实现完美转发避免这种问题,也可以完美转发。 阅读详情

C++11核心特性详解:从初始化列表到完美转发

📖 目录


一、C++11:十年磨一剑

如果你问一个C++程序员哪个版本的C++最重要,他大概率会脱口而出:C++11。

这不是没有道理的。在C++11之前,C++98/03标准已经用了整整十几年。这期间,其他编程语言如同雨后春笋般涌现:Java凭借JVM的跨平台和自动垃圾回收攻城略地,Python靠着简洁的语法和无数的第三方库异军突起,C#在Windows生态里如鱼得水。而C++呢?还是那套"老古董"标准,程序员们一边用着"落后的生产力",一边眼巴巴望着Boost库里的好东西进不了标准。

终于,在2011年,C++11标准正式发布。这个版本的更新量之大,被人戏称为"像是一门新语言"。C++之父Bjarne Stroustrup甚至说:C++11 feels like a new language.

这次更新涵盖了核心语言、标准库两大层面的海量特性。本文将从 C++11的历史背景 讲起,一路深入到 统一初始化、右值引用、移动语义、万能引用、完美转发 这些核心机制,帮助你奠定理解C++11的基础。


二、统一的列表初始化

2.1 C++98的初始化之痛

C++98里,初始化语法简直可以用"五花八门"来形容:

// C++98支持的初始化方式
int a1[] = { 1, 2, 3, 4, 5 };   // 数组可以用{}
int a2[5] = { 0 };               // 部分初始化
Point p = { 1, 2 };              // 聚合体(struct)可以用{}
int x = 2;                       // 基本类型用=

// 但是STL容器呢?
vector<int> v;                   // 只能先构造空容器
v.push_back(1);                  // 再一个个插入
v.push_back(2);
v.push_back(3);
// 没法像数组一样 vector<int> v = {1, 2, 3};  😢

你能发现什么问题?数组能用 {},结构体能用 {},偏偏 vector 不能用 {}! 这种不一致性让无数新手抓狂,也让老手在写代码时心里总绷着一根弦。

2.2 C++11:一切皆可 {}

C++11引入了 std::initializer_list,彻底统一了初始化语法:

#include <vector>
#include <map>
#include <string>
#include <iostream>
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() {
    // ===== 内置类型也支持{} =====
    int x1 = { 2 };   // C++11新支持
    int x2 = 2;       // 传统方式

    // ===== 自定义类型支持{} =====
    // {2025, 1, 1}构造一个Date临时对象,然后拷贝给d1
    // 但编译器通常会优化成直接构造d1(拷贝省略)
    Date d1 = { 2025, 1, 1 };
    Date d20(2025, 1, 1);     // 等价写法

    // 引用绑定到{}创造的临时对象上
    const Date& d2 = { 2024, 7, 25 };

    // 隐式类型转换也支持{}
    Date d3 = { 2025 };  // C++11 style
    Date d4 = 2025;      // C++98 style(单参数隐式转换)

    // ===== 省略=号也OK =====
    Point p1{ 1, 2 };
    int x3{ 2 };
    Date d6{ 2024, 7, 25 };
    const Date& d7{ 2024, 7, 25 };

    // ===== STL容器的好日子来了 =====
    vector<int> v1{ 1, 2, 3, 4 };
    vector<int> v2{ 10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
    const vector<int>& v4{ 10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

    // 函数传参时用{}简直不要太爽
    vector<Date> v;
    v.push_back(d1);                    // 传已有对象
    v.push_back(Date(2025, 1, 1));      // 传匿名对象
    v.push_back({ 2025, 1, 1 });        // {}语法——最短最直观!

    map<string, string> dict;
    dict.insert({ "xxx", "yyyy" });     // 键值对一气呵成

    // 甚至可以直接用{}初始化整个map
    map<string, string> dict2 = {
        { "xxx",  "yyyy" },
        { "sort", "zzzz" }
    };

    return 0;
}

2.3 initializer_list 究竟是何方神圣?

你以为 {1, 2, 3, 4} 是啥?它本质上就是一个 std::initializer_list<int> 对象。

initializer_list<int> il = { 10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
cout << typeid(il).name() << endl;  // 看看它的真名

initializer_list 的背后,编译器会帮你在栈上创建一个底层常量数组,然后 initializer_list 对象只是保存了指向这个数组的起始指针和长度。所以它非常轻量,拷贝一个 initializer_list 也只是拷贝指针和长度,不会拷贝实际数据。

为了让自定义容器也支持 {} 初始化,你只需要添加一个接受 initializer_list 的构造函数:

template<class T>
class list {
public:
    list(initializer_list<T> il) {
        empty_init();
        for (auto& e : il) {
            push_back(e);
        }
    }
    // ...
};

// 现在就可以这样用了!
list<int> lt0({ 1, 2, 3, 4, 5, 6 });  // 直接构造
list<int> lt1 = { 1, 2, 3, 4, 5, 6 };  // 隐式类型转换

小结:初始化列表统一了C++的初始化语法,解决了"数组能用{}容器不能用"的历史遗留问题。它让代码更简洁、更一致,是C++11最接地气的改进之一。


三、左值与右值

在深入右值引用之前,我们必须先搞清楚一个根本问题:什么算是左值,什么算是右值?

请注意:赋值号左边的不一定是左值,赋值号右边的也不一定是右值。

最靠谱的判断标准其实很简单:

能不能用 & 取地址?能取地址的就是左值,不能的就是右值。

我们来验证一下:

int main() {
    int* p = new int(0);
    int b = 1;
    const int c = b;
    *p = 10;
    string s("111111");
    s[0] = 'x';

    // 下面这些都能取地址——它们都是左值
    cout << &b    << endl;   // b是左值 ✓
    cout << &c    << endl;   // const变量也是左值 ✓
    cout << &p    << endl;   // 指针变量是左值 ✓
    cout << &(*p) << endl;   // *p解引用后是左值 ✓
    cout << &s    << endl;   // string对象是左值 ✓
    cout << (void*)&s[0] << endl;  // 字符引用是左值 ✓
}

再看看哪些不能取地址——它们就是右值:

int main() {
    double x = 1.1, y = 2.2;

    // 下面这些都不能取地址——它们都是右值
    10;              // 字面常量是右值
    x + y;           // 表达式结果是右值
    fmin(x, y);      // 函数返回值是右值
    string("11111"); // 匿名对象是右值

    // 下面这些代码全部编译报错
    // cout << &10 << endl;              // error!
    // cout << &(x + y) << endl;         // error!
    // cout << &(fmin(x, y)) << endl;    // error!
    // cout << &string("11111") << endl; // error!
}

左值 = 有明确内存地址、生命周期持续存在的对象。变量、解引用指针、数组元素等都属于左值。

右值 = 临时性的、没有稳定地址的表达式结果。字面常量、表达式临时结果、匿名对象等都属于右值。


四、左值引用与右值引用

4.1 左值引用 T&

左值引用大家都很熟悉了,它可以绑定到左值上:

int main() {
    int* p = new int(0);
    int b = 1;
    string s("111111");

    // 左值引用绑定到左值——正常工作
    int&    r1 = b;     // ✓
    int*&   r2 = p;     // ✓
    int&    r3 = *p;    // ✓
    string& r4 = s;     // ✓
    char&   r5 = s[0];  // ✓
}

普通左值引用不能直接绑定到右值:

int& r = 10;  // 编译错误!

但 const左值引用可以绑定到右值,这是C++98就有的规则:

const int&    rx1 = 10;              // ✓ const左值引用能绑定右值
const double& rx2 = 1.1 + 2.2;       // ✓
const double& rx3 = fmin(1.1, 2.2);  // ✓
const string& rx4 = string("hello"); // ✓

这个特性非常重要——它使得我们可以用const引用来接收临时对象,避免了不必要的拷贝,同时也延长了临时对象的生命周期。

4.2 右值引用 T&&

C++11引入了右值引用,它可以直接绑定到右值:

int main() {
    double x = 1.1, y = 2.2;

    int&&    rr1 = 10;              // ✓ 绑定到字面常量
    double&& rr2 = x + y;           // ✓ 绑定到表达式结果
    double&& rr3 = fmin(x, y);      // ✓ 绑定到函数返回值
    string&& rr4 = string("11111"); // ✓ 绑定到匿名对象
}

那右值引用能不能绑定到左值呢?直接绑不行,但用 std::move() 就可以绑定了。

int main() {
    int b = 1;
    int* p = new int(0);
    string s("hello");

    // 右值引用直接绑左值 → 编译错误
    // int&& rr = b;  // error!

    // 用move()把左值转成右值(xvalue),就能绑了
    int&&    rrx1 = move(b);       // ✓
    int*&&   rrx2 = move(p);       // ✓
    int&&    rrx3 = move(*p);      // ✓
    string&& rrx4 = move(s);       // ✓
    string&& rrx5 = (string&&)s;   // 等价于move——强制类型转换
}

4.3 一个极易忽略的细节

右值引用变量本身,是一个左值!

这个结论可能让你觉得绕,但它是理解后续所有内容的基础。来看看为什么:

int main() {
    int&& rr1 = 10;

    // rr1是一个右值引用变量,它有自己的名字和地址
    cout << &rr1 << endl;  // 能取地址 → rr1是左值!

    // 所以:rr1可以绑定到左值引用
    int& r6 = rr1;   // ✓ 没问题

    // 但不能再次绑定到右值引用(即使它"长着"右值引用的脸)
    // int&& rrx6 = rr1;      // error! rr1是左值

    // 如果你想再转成右值,需要move一下
    int&& rrx6 = move(rr1);   // ✓

    return 0;
}

这个"身份转换"是很多初学者踩坑的地方。记住一句话:有名字的右值引用是左值,没名字的右值(临时对象、表达式结果)才是纯右值。


五、移动语义:把深拷贝变成"资源掠夺"

5.1 问题从何而来?

来看一个场景:写一个函数返回一个大对象,或者向容器中插入一个即将销毁的对象。

// 自定义的简化版string类
namespace jys {
    class string {
    public:
        string(const char* str = "")
            : _size(strlen(str)), _capacity(_size) {
            cout << "string(char* str)——构造" << endl;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        // 拷贝构造——深拷贝,代价大!
        string(const string& s) : _str(nullptr) {
            cout << "string(const string& s) —— 深拷贝构造" << endl;
            reserve(s._capacity);
            for (auto ch : s) {
                push_back(ch);
            }
        }

        // 拷贝赋值——也是深拷贝
        string& operator=(const string& s) {
            cout << "string& operator=(const string& s) —— 深拷贝赋值" << endl;
            if (this != &s) {
                _str[0] = '\0';
                _size = 0;
                reserve(s._capacity);
                for (auto ch : s) {
                    push_back(ch);
                }
            }
            return *this;
        }

        ~string() { delete[] _str; _str = nullptr; }

        // ... 其他成员函数省略
    private:
        char* _str = nullptr;
        size_t _size = 0;
        size_t _capacity = 0;
    };
}

现在考虑这样一个函数:

class Solution {
public:
    jys::string addStrings(jys::string num1, jys::string num2) {
        jys::string str;
        // ... 计算过程,把结果存到str里 ...
        return str;  // 返回——会发生什么?
    }
};

在C++98中,return str 会触发拷贝构造——把 str 的内容完整复制一份到返回值中,然后 str 自己析构。str都要析构了,我们还要老老实实深拷贝它。

同样的问题也出现在 vector::push_back、list::push_back 等场景——我们把一个临时对象插入容器,明明原来的对象马上就没人用了,还要深拷贝一次,完全是浪费。

C++98对于这个问题束手无策,只能依赖编译器的返回值优化(RVO/NRVO)来救场,但优化不是总能生效的。

5.2 移动构造与移动赋值:不要让到嘴的鸭子飞了

C++11给出了一个彻底的解决方案:移动语义。核心思想很简单——既然你的生命周期马上就要结束,那你的东西自然就归我了,直接用就行,费那劲深拷贝干啥?

来看看移动构造和移动赋值的写法:

// 移动构造
string(string&& s) {
    cout << "string(string&& s) —— 移动构造" << endl;
    swap(s);  // 直接把s的内部资源"偷"过来
    // s现在指向原本属于我的空资源
    // s析构时会安全地delete[] nullptr
}

// 移动赋值
string& operator=(string&& s) {
    cout << "string& operator=(string&& s) —— 移动赋值" << endl;
    swap(s);  // 同理,直接交换资源
    return *this;
}

就这么简单!用 swap 交换资源的所有权——原对象的资源归我了,我原有的资源给对方(反正对方马上就要析构了,让它帮我清理垃圾)。

我们来对比一下效果:

int main() {
    jys::string s1("11111111111111111");

    // 拷贝构造:深拷贝,分配新内存 + 逐字符复制
    jys::string s3 = s1;

    // 移动构造:只交换指针,O(1)复杂度!
    jys::string s4 = jys::string("222222222");

    // move(s1)把s1强制转成右值,触发移动构造
    // 注意:之后s1就变成空的了,不要再使用
    jys::string s5 = move(s1);

    return 0;
}

输出:

string(char* str)——构造         ← s1的构造
string(const string& s) —— 深拷贝构造  ← s3 = s1,深拷贝
string(char* str)——构造         ← 匿名对象构造
string(string&& s) —— 移动构造  ← s4,移动走匿名对象的资源
string(string&& s) —— 移动构造  ← s5,移动走s1的资源

移动构造只做了一件事:交换指针。深拷贝的O(n)直接降到了O(1),这对于大对象来说是天壤之别。

5.3 回到最初的例子

还记得那个 addStrings 函数吗?在C++11下:

int main() {
    // 妙啊——函数内局部变量str在return时,
    // 编译器会优先选择移动构造(如果已定义的话)
    jys::string ret = Solution().addStrings(
        "11111111111111111111",
        "222222222222222222222222222"
    );
    cout << ret.c_str() << endl;
    return 0;
}

有了移动构造,即使编译器不做RVO优化,return一个局部变量也是移动而非拷贝——因为局部变量在return时会被自动识别为右值。

再来看一个传参和插入容器的场景:

int main() {
    jys::string s1("11111111111");

    vector<jys::string> v;

    v.push_back(s1);             // s1是左值 → 拷贝
    v.push_back(move(s1));       // move后是右值 → 移动!
    v.push_back("22222222222");   // 临时对象是右值 → 移动!

    return 0;
}

小结:移动语义让C++程序在不改变使用方式的前提下获得了巨大的性能提升。STL容器(vector、string、map等)在C++11中全面支持了移动语义,很多以前必须深拷贝的场景现在自动变成了廉价的指针交换。


六、万能引用与引用折叠

6.1 一个神奇的现象

先来看两行代码:

template<class T>
void f1(T& x) {}   // 只能接收左值

template<class T>
void f2(T&& x) {}  // 既能接收左值,也能接收右值?!

诶,T&& 不是右值引用吗?怎么 f2 既能接收左值也能接收右值?

int main() {
    int n = 0;

    f2(n);   // n是左值 → 居然可以!实参类型变成 int&
    f2(0);   // 0是右值 → 实参类型是 int&&
}

这就是C++11中万能引用(Universal Reference)。规则是这样的:

当 T&& 出现在模板中,且 T 是被推导的类型参数时,它就不是普通的右值引用,而是万能引用——它可以根据传入的实参类型,自动变成左值引用或右值引用。

具体来说:

  • 传入左值 → T 推导为 int&,T&& 变成 int&
  • 传入右值 → T 推导为 int,T&& 变成 int&&

6.2 引用折叠——幕后的规则

万能引用的背后是一套叫**引用折叠(Reference Collapsing)**的规则。C++不允许"引用的引用",但当模板推导产生这种情况时,编译器会按照以下规则折叠:

折叠前折叠后
T& &T&
T& &&T&
T&& &T&
T&& &&T&&

也就是说,只有两个都是右值引用时,结果才是右值引用;其他情况统统是左值引用。

来看具体的例子验证:

void f1(T& x) {}   // 模板参数是 T&
void f2(T&& x) {}  // 模板参数是 T&&

int main() {
    typedef int& lref;
    typedef int&& rref;
    int n = 0;

    // ===== 引用折叠演示 =====
    lref&  r1 = n;   // int& &  → int&
    lref&& r2 = n;   // int& && → int&
    rref&  r3 = n;   // int&& & → int&
    rref&& r4 = 1;   // int&& && → int&&

    // ===== f1(T& x)的实例化情况 =====
    f1<int>(n);          // T=int,void f1(int& x)
    // f1<int>(0);       // error! int& 不能绑右值
    f1<int&>(n);         // T=int&,折叠后 void f1(int& x)
    // f1<int&>(0);      // error! int& 不能绑右值
    f1<int&&>(n);        // T=int&&,折叠后 void f1(int& x)
    // f1<int&&>(0);     // error! int& 不能绑右值
    f1<const int&>(n);   // T=const int&,void f1(const int& x)
    f1<const int&>(0);   // ✓ const int& 可以绑右值

    // ===== f2(T&& x)的实例化情况 =====
    // f2<int>(n);       // error! int&& 不能绑左值
    f2<int>(0);          // T=int,void f2(int&& x)
    f2<int&>(n);         // T=int&,折叠后 void f2(int& x)
    // f2<int&>(0);      // error! int& 不能绑右值
    // f2<int&&>(n);     // error! int&& 不能绑左值
    f2<int&&>(0);        // T=int&&,void f2(int&& x)
}

6.3 自动推导到底推了什么

对于万能引用 T&&,模板实参的推导遵循这个规则:

template<class T>
void Function(T&& t) {
    int a = 0;
    T x = a;       // 这行可以用来"探测"T到底是什么
    cout << &a << endl;
    cout << &x << endl;
}

int main() {
    int a = 0;

    Function(10);            // 右值 → T=int      → void Function(int&& t)
    Function(a);             // 左值 → T=int&     → void Function(int& t)
    Function(std::move(a));  // 右值 → T=int      → void Function(int&& t)

    const int b = 8;
    Function(b);             // const左值 → T=const int& → void Function(const int& t)
    Function(std::move(b));  // const右值 → T=const int  → void Function(const int&& t)

    return 0;
}

注意 const int b 的情况——const 属性是不会丢的,推导时 const 会被保留在 T 中。如果 Function 内部尝试对 x 做修改操作,当传入 const int b 时就会编译报错。


七、完美转发——属性一个都不能少

7.1 为什么要"转发"?

万能引用虽然能同时接收左值和右值,但有一个致命的问题——右值引用变量本身是左值。这意味着:

void Fun(int& x)       { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x)      { cout << "右值引用" << endl; }
void Fun(const int&& x){ cout << "const 右值引用" << endl; }

template <class T>
void Function(T&& t) {
    Fun(t);  // 不管传进来的是啥,t本身是左值,Fun(t)永远调左值版本!
}

int main() {
    Function(10);            // 传右值,希望调Fun(int&&),实际调了Fun(int&) 😭
    Function(std::move(a));  // 同样被当左值处理

    return 0;
}

传进来的明明是右值,为什么转发出去就变成左值了?

问题的根源就是右值引用变量 t 本身是一个左值(我们讲过)。t 有了名字,就可以取地址,所以 Fun(t) 永远会匹配到 Fun(int&)。

我们需要一种机制,能够保持原始参数的"左值/右值"属性不变地转发给下一个函数——这就是完美转发(Perfect Forwarding)。

7.2 std::forward——属性还原术

std::forward<T>(t) 的用法很简单:

template <class T>
void Function(T&& t) {
    Fun(forward<T>(t));  // 用forward把t的属性"恢复"回去
}

int main() {
    // 右值 → T=int     → forward<int>(t)  → 右值 → Fun(int&&)
    Function(10);

    int a;
    // 左值 → T=int&    → forward<int&>(t) → 左值 → Fun(int&)
    Function(a);

    // 右值 → T=int     → forward<int>(t)  → 右值 → Fun(int&&)
    Function(std::move(a));

    const int b = 8;
    // const左值 → T=const int& → forward<const int&>(t) → const左值 → Fun(const int&)
    Function(b);

    // const右值 → T=const int  → forward<const int>(t)  → const右值 → Fun(const int&&)
    Function(std::move(b));

    return 0;
}

输出:

右值引用
左值引用
右值引用
const 左值引用
const 右值引用

完美!传入什么属性,传出什么属性,一丝一毫都没有丢失。这就是"完美"的含义。

7.3 forward 是怎么做到的?

forward 的源码其实非常精简(简化版):

// forward一个左值 → 返回左值引用
template<class T>
T&& forward(typename remove_reference<T>::type& arg) noexcept {
    return static_cast<T&&>(arg);
}

// forward一个右值 → 返回右值引用
template<class T>
T&& forward(typename remove_reference<T>::type&& arg) noexcept {
    return static_cast<T&&>(arg);
}

关键在于 static_cast<T&&>(arg) 结合引用折叠规则:

调用方式T的类型T&&折叠后返回
forward<int>(arg)intint&&右值引用
forward<int&>(arg)int&int&左值引用
forward<int&&>(arg)int&&int&&右值引用

T 的类型由调用者通过模板推导或显式指定,完美保留原始参数的值类别属性。

7.4 实战:在list中用完美转发优化插入

来看看完美转发在实际容器类中的应用。下面是一个简化版的list实现,它的 push_back 和 insert 都用了完美转发:

template<class T>
struct list_node {
    T _data;
    list_node<T>* _next;
    list_node<T>* _prev;

    // 万能引用 + forward → 根据实参属性选择拷贝或移动
    template<class X>
    list_node(X&& data)
        : _data(forward<X>(data))  // 左值→拷贝,右值→移动
        , _next(nullptr)
        , _prev(nullptr)
    {}
};

template<class T>
class list {
public:
    // 万能引用版本——一个函数替代了 const T& 和 T&& 两个重载
    template<class X>
    void push_back(X&& x) {
        insert(end(), forward<X>(x));
    }

    template<class X>
    iterator insert(iterator pos, X&& x) {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* newnode = new Node(forward<X>(x));

        newnode->_next = cur;
        cur->_prev = newnode;
        newnode->_prev = prev;
        prev->_next = newnode;

        ++_size;
        return newnode;
    }
    // ...
};

使用起来非常自然:

int main() {
    jys::list<jys::string> lt;

    jys::string s1("11111111111");
    lt.push_back(s1);              // s1是左值 → 拷贝构造

    jys::string s2("33333333333");
    lt.push_back(move(s2));        // move后是右值 → 移动构造

    lt.push_back("22222222222");   // 字面量是右值 → 移动构造

    return 0;
}

一个函数模板,同时处理了拷贝和移动两种情况——这就是完美转发 + 万能引用的威力。STL中所有容器的 push_back、emplace_back、insert 等方法都是这样实现的。


八、总结

本文从C++11的历史背景出发,沿着一条清晰的逻辑链讲解了以下核心内容:

初始化列表 (initializer_list)
    ↓
左值与右值的本质区别(能取地址吗?)
    ↓
左值引用 T& vs 右值引用 T&&(能绑啥不能绑啥)
    ↓
移动构造与移动赋值(把深拷贝变"换家")
    ↓
万能引用 T&& + 引用折叠规则(模板中的变色龙)
    ↓
完美转发 forward<T>(属性一个不少地传下去)

这些特性不是孤立存在的——它们是C++11为提升性能和简化代码而精心设计的一整套体系:

  • 初始化列表让语法更统一
  • 右值引用让"识别临时对象"成为可能
  • 移动语义让临时对象的传递不再昂贵
  • 万能引用+完美转发让泛型代码能写得更简洁且保持高效

读到这里,你对C++11语法的理解是不是更清晰了呢?


如有疏漏或不当之处,欢迎在评论区交流指正。

C++ 网络库 cpp-netlib的简单使用 示例代码 (C++11, cpp-netlib版本:0.12.0) 需要链接库文件 target_link_libraries(app boost_system network-uri cppnetlib-server-parsers ) #include <network/uri.hpp> #include <nlohmann/json.hpp... 阅读详情

相关推荐

Dev-Cpp 5.11:经典C++集成开发环境安装指南

本文还有配套的精品资源,点击获取 简介:Dev-Cpp 5.11是一个开源的C++集成开发环境,针对Windows操作系统的开发者设计。安装包包含一个可执行安装程序,用户通过解压和运行它可以安装Dev-Cpp。该IDE提供了包括源代码编辑器、TDM-GCC编译器套件、项目管理工具、调试器以及诸多辅助工具在内的完整开发平台。虽然Dev-Cpp 5.11是一个稳定的老版本,但它...

weixin_36289742的博客 8209

【CPP11?】结合CPP发展历史来理解CPP11

由C到CPP的大概历史是如何的?CPP是一门怎样的语言?想必浏览本文将会对此有一些初步的认识,本文是结合历史来介绍CPP的发展历程的。

BinaryWiker的博客 1261

手把手教你用UC3844设计多路输出反激电源(附PCB布局避坑指南)

本文详细介绍了使用UC3844设计多路输出反激电源的实战技巧,包括核心电路设计、变压器参数计算、PCB布局优化及EMI抑制方法。通过实验室实测数据和项目经验,帮助工程师避开常见设计陷阱,提升电源性能和可靠性。特别附赠PCB布局避坑指南,助力高效完成开关电源设计。

weixin_30300225的博客 568

C++11(上):重塑 C++ 的现代革命

C++11核心特性解析:现代C++的革新与优化 本文深入剖析了C++11标准带来的革命性升级,重点介绍了三大核心特性: 列表初始化:实现统一初始化语法,支持内置类型和自定义类型的{}初始化,并引入std::initializer_list机制简化容器初始化 右值引用: 区分左值/右值概念 支持移动语义(移动构造和移动赋值) 有效解决深拷贝性能问题 优化传值返回场景 移动语义应用: STL容器新增右值引用版本接口 通过移动而非拷贝提升性能 特别适用于临时对象处理场景 这些特性使C++在保持高性能的同时,显著提

心向往之,行必能至 2364

OpenPCDet安装及其3D检测算法实现

OpenPCDet及pointpillar复现

weixin_58207716的博客 1万+

AI搜索优化(GEO)实战指南:抢占智能对话流量入口

AI SEO,是针对生成式AI平台(如DeepSeek、豆包、Kimi等)的优化策略,旨在通过适配AI算法对内容的理解和引用逻辑,提升品牌在AI生成答案中的可见性与权威性,从而在对话式搜索场景中抢占流量入口。:强化内容经验性(Experience)、专业性(Expertise)、权威性(Authoritativeness)、可信度(Trustworthiness)内容需避免夸大描述,提供可验证的细节(如数据、用户评价)。

Baixue5209的博客 6773

C++发展

C++ 是由于 1979 年在贝尔实验室(Bell Labs)开始开发的,最初是作为 C 语言的一个扩展,目的是在不丧失 C 语言高效性的基础上,提供面向对象编程的特性。C++ 的早期发展1979-1985C++ 标准化过程1985-1998C++ 标准演化2003-2011C++112011年C++142014年C++172017年C++202020年C++232023年。

ljw的博客 3807

一文搞懂蚁群算法Ant Colony Optimization(ACO)!!!(灵感起源+数学原理+代码实现+实战应用)

本文系统介绍了蚁群算法的原理与实现。首先从蚂蚁觅食行为出发,阐释了信息素正反馈机制如何引导群体找到最短路径。随后详细解析了算法的数学模型,包括状态转移规则和信息素更新机制。通过MATLAB代码演示了算法在旅行商问题(TSP)中的应用,展示了路径构建和优化的完整流程。最后讨论了算法的优势、局限性和实际应用场景。蚁群算法通过模拟生物智能,为解决复杂优化问题提供了一种高效方法,其核心在于信息素的正反馈机制与随机探索的平衡。

qq_42212808的博客 887

C++ | C++11新特性解析:现代编程的里程碑

在2003年,C++标准委员会提交了一份技术勘误表(TC1),使得C++03成为C++98之后的最新标准。然而,C++03主要是对C++98标准中的漏洞进行修复,语言的核心部分并没有大的改动。因此,人们通常将这两个标准合并称为C++98/03标准。从C++0x到C++11,C++标准经过了长达十年的发展,带来了约140个新特性和对C++03标准中约600个缺陷的修正。这使得C++11更像是从C++98/03中孕育出的一种新语言。

LiHongyu05的博客 2273

【麒麟系统】Rsync文件复制与同步工具

本文摘要: Rsync是一款Linux/Unix系统标配的高效文件同步工具,支持增量传输、SSH加密、断点续传等核心功能,相比SCP具有显著优势。文章详细解析了Rsync的五种标准语法格式和常用参数组合,重点介绍了本地同步、远程SSH同步(推送/拉取)的实操方法,并提供了密钥免密登录配置方案。针对企业运维场景,还讲解了文件过滤、镜像同步(--delete)、迁移同步(--remove-source-files)、空运行预校验(--dry-run)等高级功能,以及带宽限速等优化技巧,帮助用户安全高效地完成各类

信创运维工程师 · 银河麒麟 & 容器实战 | 金融生产环境迁移·等保加固 | 142篇原创·17万+阅读 997

FineReport实战-- 国内外各地区销售数据分析 多个图表联动

进行国内外各地区业务员销售数据分析是一项复杂但极具价值的任务,它可以帮助企业了解不同市场的表现,并据此调整销售策略以提高整体绩效。实现功能:同一个报表里面多个图表联动问题。

Davina_yu的博客 1196

【C++】11 Visual Studio 2019 C++安装matplotlib-cpp绘图

使用C++调用matplotlib-cpp绘图

记录专业模型开发、算法程序设计、软件学习笔记、生活日志。 9392

别再让 AI 瞎写 SAP 代码了!这套 Claude Code Skill 的验证机制才是关键

本文深入解析了专为SAP CAP开发设计的AI辅助工具sap-cap-capire的技术架构。该工具采用四层设计:协议层集成MCP和LSP实现实时模型访问与语法检查;智能层通过4个专业化Agent(数据建模、服务开发等)分工协作;验证层确保代码质量;知识层存储文档模板。这种工程化设计解决了通用AI在CAP开发中面临的上下文不足、专业性缺失和代码质量保证等核心问题,通过本地化服务和实时反馈机制显著提升了开发效率。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 878

【深度剖析 C++11】 第一弹:现代 C++ 编程的基石与革新

还在为C++11复杂错乱的语法发愁吗?解锁 C++11 的秘籍就在这里!C++11 看这一篇就够了,带你一起深度解析有关C++11的各种新语法,新特性,成为一个合格的C++学习者!

bite_zwy的博客 2619

机器学习实战7-基于机器学习算法预测相亲成功率

大家好,我是微学AI,今天给大家介绍一下机器学习实战7-基于机器学习算法预测相亲成功率,随着社会的发展,大家都忙于事业,对自己的终身大事就耽搁了,相亲是一种传统的寻找伴侣的方式,随着时代的发展,相亲的方式也在不断地改变。在这个过程中,了解相亲双方的信息以及预测相亲是否成功变得越来越重要。本文将介绍如何使用随机森林算法对相亲成功进行预测,通过分析男女双方的房子、车子、长相、家庭条件、父母情况、生活习惯、学历、性格、兴趣等因素,来预测相亲是否成功。

微学AI的博客 3026

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

C++之旅-C++11的深度剖析(1)

随着现代软件开发的快速发展,编程语言也在不断进化,C++ 作为一种功能强大的编程语言,已经经历了多个版本的更新,每一次版本的发布都为开发者带来了新的特性和功能。C++11是 C++ 语言的一个重要版本,它于 2011 年正式发布,并引入了许多全新的特性,极大地提升了代码的效率、可读性以及程序的执行性能。C++11 的发布不仅对 C++ 开发者来说是一次重大提升,它还为整个编程社区提供了更加现代化的编程工具。

2302_79376097的博客 3869

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

C++20 核心特性详解:现代C++的进化里程碑

《C++20革命性特性解析》摘要:C++20标准引入了概念(Concepts)、模块(Modules)、协程(Coroutines)三大核心特性,彻底革新了模板编程、代码组织和异步处理方式。同时新增范围库(Ranges)、格式化库(std::format)等现代化工具,大幅提升开发效率和代码安全性。文章通过Mermaid图表直观展示特性关系,并分析各编译器支持现状。这些变革使C++20成为现代系统编程的分水岭,为高性能计算注入新活力。

ALex_zry的博客 1595

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++ 发展历程全解析

从最初的小众实验性语言到如今广泛应用于各个领域的主流编程语言,C++ 走过了漫长而辉煌的道路。每一次标准的更新都凝聚着无数开发者的心血和智慧,也为后来者铺平了前进的道路。相信在未来,C++ 将继续发挥其独特的优势,在软件开发领域占据重要地位。

2301_81482480的博客 1716

学懂C++(六十):C++ 11、C++ 14、C++ 17、C++ 20新特性大总结(万字详解大全)

C++ 11、C++ 14、C++ 17、C++ 20新特性总结(万字详解大全)

编程技术探索者,分享C/C++、C#、Java、数据库等开发经验,聚焦实战技巧与AI兴趣,助力编程爱好者成长。 4806

C++语言的发展史&各版本发展

C++语言的发展史是一部不断进化和完善的历程。自1983年由Bjarne Stroustrup在贝尔实验室创立以来,C++从“C with Classes”逐渐发展为一种集面向过程、面向对象、泛型编程等多种编程范式于一体的复杂编程语言。其发展历程经历了多个关键阶段和版本更新,包括C++98、C++03、C++11、C++14、C++17和C++20等。每个版本都引入了新的特性和改进,如C++11中的自动类型推导、Lambda表达式,C++20中的概念、模块和协程等,极大地增强了C++语言的表达能力和编程效率

byte轻骑兵的技术小窝 1万+

C++11 新特性:重塑C++编程的里程碑

C++11作为C++语言的重要里程碑,引入了多项革新特性,显著提升了开发效率和代码安全性。核心特性包括自动类型推导(auto/decltype)简化复杂类型声明,Lambda表达式优化回调函数编写,范围for循环简化容器遍历,nullptr解决指针歧义问题,以及强类型枚举(enum class)增强类型安全。内存管理方面,智能指针(unique_ptr/shared_ptr/weak_ptr)实现了自动内存管理,有效防止内存泄漏。这些特性共同推动了C++向现代化编程语言的转型,使其在保持高性能的同时,提高了

bkspiderx的博客 1072

C++学习之旅【深入回溯C++11的发展历程】

本文回顾了C++11的发展历程,重点介绍了列表初始化的演进。C++11通过统一初始化方式,实现了一切对象皆可用{}初始化,包括内置类型和自定义类型。文章详细讲解了std::initializer_list的作用,它使容器支持任意多个值的初始化,简化了代码编写。通过示例代码展示了{}初始化在实际编程中的应用,如vector等容器的初始化方式,体现了C++11在语法简洁性和开发效率上的重大改进。

承于所学,探技术之渊;以代码求政,寻成长之道。 1134

Cpp入门基础

定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下 ⾯的rand不在冲突了。C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/ 类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。

2401_86658278的博客 5079

FreeRTOS_cpp11:为嵌入式开发带来C++11多线程支持

FreeRTOS_cpp11:为嵌入式开发带来C++11多线程支持 项目介绍 FreeRTOS_cpp11 是一个开源项目,它为 FreeRTOS 实时操作系统提供 C++11 标准的多线程支持。这意味着开发者可以在嵌入式系统中使用 C++11 中的 std::thread、std::mutex、std::condition_variable、std::chrono、std::future 等高级...

gitblog_00675的博客 1292

《cpp11 安装与配置指南》

《cpp11 安装与配置指南》 1. 项目基础介绍 cpp11 是一个开源项目,旨在帮助用户使用 C++ 代码与 R 对象进行交互。这个项目的目标是提供一个类似于 Rcpp 的工具,但采用了不同的设计权衡和新特性。它的语法与 Rcpp 相似,但是提供了一个从头开始的 C++ 绑定重写,包含了一些改进和新的功能。 主要编程语言:C++ 和 R。 2. 项目使用的关键技术和框架 C++11 特性:c...

gitblog_01101的博客 1179

探索 Zhangwenxiao 的 Cpp11WebServer: 一个现代化C++构建的HTTP服务器

探索 Zhangwenxiao 的 Cpp11WebServer: 一个现代化C++构建的HTTP服务器 在现代互联网世界中,高效的HTTP服务器是任何web应用的基础。今天,我们将会深入探讨一个由Zhangwenxiao开发并维护的开源项目——,这是一个利用C++11特性构建的轻量级HTTP服务器。 项目简介 Cpp11WebServer是一个基于C++11标准库实现的简单且快速的web服务器。...

gitblog_00030的博客 813
上一篇: 封装哈希表模拟实现 unordered_map 和 unordered_set
下一篇: C++11:从可变参数模板到bind绑定器
jinyishu_
博客等级 码龄1年 53粉丝 82原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值