C++14新特性个人总结

C++14 是继 C++11 之后的第一个小更新版本(被称为 "C++11 的小兄弟"),主要目标是完善 C++11 的特性、修复缺陷、提供更好的编译期编程支持以及增加一些实用的小改进,而不是引入革命性的新概念。它让代码更简洁、更泛型、更强大。

1.Variable template(变量模板)

C++14 引入了 变量模板(variable templates),让你可以像写函数模板、类模板那样,把 变量本身也做成模板
通俗地说:以前模板只能生成“函数”或“类”,现在还能生成“变量”。

template <模板形参列表>
变量类型 变量名 = 初始化表达式;
  • 模板形参:可以是类型参数 typename T,也可以是非类型参数 int N 等。
  • 变量名:实例化后就是真正的全局/命名空间作用域变量。
  • 初始化表达式:通常用 constexpr,让变量在编译期就能求值。

例子:

#include <iostream>

template<typename T>
constexpr T pi_v = T(3.1415926535897932385L);   // 变量模板

int main() {
    std::cout << pi_v<int> << '\n';      // 3
    std::cout << pi_v<double> << '\n';   // 3.14159...
}

非类型参数

template<int N>
int arr_size = N * 2;

int a = arr_size<5>;   // a == 10

总结:变量模板让“常量”也能泛型化,是 C++14 中一个简洁却强大的小特性。

2.generic lambdas(通用 Lambda

C++11的lambda必须写出具体类型

auto add_int   = [](int   a, int   b) { return a + b; };
auto add_float = [](float a, float b) { return a + b; };

类型一变就要重新写一边。

C++14在形参位置直接写 auto,编译器会为 lambda 生成一个 带模板 operator() 的闭包类

auto add = [](auto a, auto b) { return a + b; };

等价于手写

struct __lambda_add {
    template<typename T, typename U>
    auto operator()(T a, U b) const { return a + b; }
};
__lambda_add add{};

完全类型擦除,按需实例化

总结C++14 Generic Lambda = λ 语法糖 + 模板实例化机制,让你用最少代码写出类型安全的“多态”闭包。

3.lambda init-capture(初始化捕获,又称广义 lambda 捕获)

在 C++11 中,捕获列表只能写两种东西:

  • [name] —— 按值或按引用捕获已存在的局部变量
  • [=] / [&] —— 按值或按引用捕获所有可见局部变量

当有需要捕获移动变量的时候会失败,比如:unique_ptr 只能移动,不能拷贝,[=] 会尝试拷贝(unique_ptr 的拷贝构造函数被删除)

初始化捕获:

[变量名 = 初始化表达式] { ... }
  • 初始化表达式可以是任意表达式,包括 std::move、函数调用结果、字面量等。
  • 生成的数据成员直接由该表达式初始化,无需外部变量。
  • 因此也支持“无中生有”的捕获:

修复前面的 unique_ptr 例子:

auto p = std::make_unique<int>(42);
auto lam = [p = std::move(p)] { return *p; };   // OK:p 被移动到闭包对象里

总结:Init-Capture 的出现主要是为了解决 “只能移动、不能拷贝的对象无法被 lambda 捕获” 这一根本痛点,并顺带弥补了捕获列表无法当场计算、无法就地声明新变量等表达能力缺陷

4.relaxed restrictions on constexpr functions(放宽限制的constexper)

  • 这是 C++14 在编译期编程方面最重要的增强。极大地扩展了可以在 constexpr 函数中使用的功能:
    • 允许声明局部变量(C++11只能有单个 return 语句)。
    • 允许使用 ifswitch 等控制流语句。
    • 允许使用任何类型的循环 (for, while, do-while)。
    • 允许修改在函数内部创建的对象(只要该对象的生命周期在 constexpr 函数求值期间开始和结束)。
    • 允许函数有多个 return 语句。
  • 这使得编写更复杂、更实用的编译期计算和类型操作成为可能。
constexpr int factorial(int n) {
    if (n <= 0) return 1;
    int result = 1; // 允许局部变量
    for (int i = 1; i <= n; ++i) { // 允许循环
        result *= i;
    }
    return result;
}
constexpr int fac10 = factorial(10); // 编译期计算

总结:从 C++11 到 C++14,“relaxed restrictions on constexpr functions” 主要把 “只能写一条 return 的纯表达式函数” 放宽成了 “几乎可以写普通 C++ 代码的函数”,从而让编译期计算从“函数式小玩具”变成了“可读写局部变量、有循环、有分支的正经程序”。

5.binary literals(二进制字面量)

没啥好说的,某些情况会用到

  • 允许使用 0b0B 前缀表示二进制整数常量。
int mask = 0b11010101; // 二进制表示

6.digit separators(数字分隔符)

  • 允许在数字常量中使用单引号 (') 作为分隔符以提高长数字的可读性。分隔符对数值没有影响。
long bigNumber = 1'000'000'000;
double pi = 3.14159'26535;
int hexBytes = 0xFF'FF'00'00;
int binMask = 0b1010'1100'0011'1101;

7.return type deduction for functions(函数返回类型推导)

  • 允许使用 auto 作为普通函数的返回类型,编译器根据函数体内的 return 语句推导返回类型。要求函数的所有 return 语句必须推导出相同的类型。
  • 简化了模板函数和返回类型复杂的函数的编写。注意: 在类定义中,如果成员函数使用 auto 返回类型且定义在类体内,它需要是 inline 的(通常直接在类体内定义即可)。
template 
auto sum(T a, U b) {
    return a + b;
}
auto getValue(bool flag) {
    if (flag) {
        return 10;   // 返回 int
    } else {
        return 3.14; // 错误!返回类型不一致 (double vs int)
    }
}

8.aggregate classes with default non-static member initializers(聚合体的成员初始化

  • 允许聚合体(如没有用户声明构造函数、私有/保护基类或非静态数据成员的类、结构体、数组)的成员使用大括号或等号初始化器进行初始化。这简化了聚合体的初始化列表构造。
struct Point {
    int x;
    int y = 10; // C++14 允许成员初始化器
};
Point p1 = {1};      // p1.x = 1, p1.y = 10 (使用默认值)
Point p2 = {2, 20}; // p2.x = 2, p2.y = 20

9.std::make_unique

  • 标准库增加了 std::make_unique 模板函数,用于创建 std::unique_ptr 对象。这完善了智能指针工具集(std::make_shared 在 C++11 已有),提供更安全、更高效(通常)且更简洁(避免显式 new)的方式来创建独占所有权的对象。非常常用!
// C++11: 需要直接使用 new
std::unique_ptr ptr11(new Widget());
// C++14: 更安全、简洁
auto ptr14 = std::make_unique();

总结:std::make_unique异常安全、代码简洁、避免裸 new 三件事一次搞定,是 C++14 之后创建 unique_ptr 的首选方式;只有在需要自定义删除器或 C++14 里创建动态数组时才退而求其次。

10.std::shared_timed_mutex and std::shared_lock

std::shared_timed_mutex:

  • 共享读取:多个线程可以同时获取共享锁(读取锁),允许并发读取数据。
  • 独占写入:在写操作时,会独占资源,防止并发写入数据造成数据竞争。
  • 超时支持:支持超时等待功能,可以在一定时间内获取锁或放弃操作。

std::shared_lock:

  • 自动加锁和解锁:std::shared_lock在构造时自动获取共享锁,在析构时自动释放共享锁,保证锁的正确使用。
  • 共享锁支持:std::shared_lock可以管理std::shared_timed_mutex的共享锁,支持多线程并发读取数据。
  • 适用于多读单写场景:适用于多个线程读取数据,但需要独占写入数据的场景,提高读操作的并发性能。

总结:std::shared_timed_mutex 是一把支持共享读 / 独占写可定时互斥量;
std::shared_lock专门配合它的 RAII 共享读锁(当然也能配合其他共享互斥量)。

11.std::integer_sequence

在编译期生成一个只包含整数的不可变数组”,专门用来解包模板参数推动折叠表达式实现编译期循环

模板方面的,跳过看不懂。

12.std::exchange

  • std::exchange 原子地将一个新值赋给对象,并返回其旧值。常用于移动赋值运算符等场景。

    template 
    T exchange(T& obj, U&& new_value) {
        T old_value = std::move(obj);
        obj = std::forward(new_value);
        return old_value;
    }
    

13.std::quoted

一句话
把字符串按“带引号、转义内部引号”的格式输入/输出,读写 CSV/JSON/日志时再也不用手工处理引号和反斜杠。

posted @ 2025-08-03 17:52  桂洛克船长  阅读(161)  评论(0)    收藏  举报