C++20新特性_std::format和span

文章目录

  • [第二章 C++20标准库特性](#第二章 C++20标准库特性)
    • [2.1 std::format](#2.1 std::format)
      • [2.1.1 基本语法](#2.1.1 基本语法)
      • [2.1.2 位置参数](#2.1.2 位置参数)
      • [2.1.3 格式说明符](#2.1.3 格式说明符)
      • [2.1.4 总结](#2.1.4 总结)
    • [2.2 std::span](#2.2 std::span)
      • [2.2.1 span原理](#2.2.1 span原理)
      • [2.2.2 切片功能](#2.2.2 切片功能)
      • [2.2.3 读取字节](#2.2.3 读取字节)
      • [2.2.4 悬空引用](#2.2.4 悬空引用)
      • [2.2.5 span总结](#2.2.5 span总结)

本文记录C++20新特性之std::format和span。

第二章 C++20标准库特性

2.1 std::format

在C++20之前,文本格式化主要依赖于 C 风格的 printf函数或 C++ 的 iostream 流操作。printf 缺乏类型安全且难以扩展,而 iostream 虽然类型安全但语法繁琐且性能不高。

C++20引入了库,提供了一个类型安全、高效且语法类似于 Python 字符串格式化的现代解决方案。

2.1.1 基本语法

std::format 的核心思想是使用花括号 {} 作为占位符。它返回一个 std::string。

示例1:{}用法说明。

cpp 复制代码
    void test()
    {
        std::string name = "wangandy";
        int id = 330;

        std::string message = std::format("我叫 {}!,id 为 {}", name, id);

        std::cout << message << endl;
        // 我叫 wangandy!,id 为 330
    }

需要注意的是,{}中不能加空格等不符合语法的字符。

2.1.2 位置参数

可以通过在花括号中指定索引来控制参数的参入顺序。

示例2:下面的示例中在{}中指定参数顺序。

cpp 复制代码
    void test()
    {
        std::string name = "wangandy";
        int id = 330;

        std::string message = std::format("我叫 {0}!,id 为 {1}", name, id);
        std::cout << message << endl;
        // 我叫 wangandy!,id 为 330

        std::string message2 = std::format("我叫 {1}!,id 为 {0}", name, id);
        std::cout << message2 << endl;
        // 我叫 330!,id 为 wangandy
    }

2.1.3 格式说明符

std::format支持丰富的格式化选项,语法如下:

cpp 复制代码
{:[fill] [align] [sign] [#] [0] [width] [.precision] [type] }

{} : 替换字符的标志,其中包含要格式化的参数索引和格式说明。

":" : 分割符,用于分隔参数索引和格式说明。

示例:下面测试每个符号的用法。

cpp 复制代码
    void test()
    {
        // 1. width 和 align (宽度和对齐)
        //    >10 表示右对齐,总宽度为10
        std::cout << std::format("右对齐: |{:>10}|", "hi") << std::endl;
        // 输出: 右对齐: |        hi|

        // 2. fill 和 align (填充和对齐)
        //    *^10 表示居中对齐,总宽度为10,用*填充
        std::cout << std::format("居中填充: |{:*^10}|", "hi") << std::endl;
        // 居中填充: |****hi****|
    
         // 3. sign (符号)
        std::cout << std::format("显示符号: {:+} 和 {:+}", 12, -12) << std::endl;
        // 输出: 显示符号: +12 和 -12
        std::cout << std::format("空格符号: {:} 和 {:} ", 12, -12) << std::endl;
        // 输出: 空格符号:  12 和 -12

        // 4. # (替代形式) 和 type (类型)
        std::cout << std::format("十六进制: {0:x}, {0:#x}, {0:#X}", 12) << std::endl;
        // 输出: 十六进制: c, 0xc, 0XC
        std::cout << std::format("二进制: {0:b}, {0:#b}", 12) << std::endl;
        // 输出: 二进制: 1100, 0b1100
    
        // 5. 0 (零填充)
        std::cout << std::format("零填充: {:08d}", 123) << std::endl;
        // 输出: 零填充: 00000123
    
        // 6. .precision (精度)
        std::cout << std::format("浮点数精度: {:.2f}", 3.14159) << std::endl;
        // 输出: 浮点数精度: 3.14
        std::cout << std::format("字符串精度: {:.5s}", "hello world") << std::endl;
        // 输出: 字符串精度: hello

        // 7. 综合示例
        double temperature = 25.6789;
        std::cout << std::format("温度: |{:+010.2f}|", temperature) << std::endl;
        // 输出: 温度: |+000025.68|
        // 解释:
        //   + : 显示正号
        //   0 : 使用0填充
        //   10: 总宽度为10
        //   .2: 小数点后保留2位
        //   f : 浮点数类型
    }

2.1.4 总结

性能:format的性能优于iostream,在某些情况下超过 printf.

语法:format结合了python风格的易用性和C++的高性能,是现代C++开发中处理字符串的首选工具。

2.2 std::span

在C++20之前,我们需要连续内存块保存数据时,通常使用vector或者C风格的数组。编写一个函数处理这个数组时,在函数中传递这个数组,如下:

cpp 复制代码
// 方式 A: 只能接受 vector,不能接受原生数组或 std::array
void print(const std::vector<int>& v); 

// 方式 B: C 风格  传递指针 和 长度
void print(const int* ptr, size_t size); 

C++20中,可以直接使用span输出,它充当了不同容器类型之间的通用接口。

cpp 复制代码
    void print(std::span<int> s)
    {
        for (int x : s)
        {
			std::cout << x << " ";
        }
		std::cout << std::endl;
    }

    void test()
    {
        int arr[] = { 1,2,3 };
		vector<int> vec = { 4,5,6,7 };
		std::array<int, 3> stdarr = { 8,9,10 };

        print(arr);      // 从原生数组创建 span
        print(vec);      // 从 vector 创建 span
        print(stdarr);   // 从 std::array 创建 span
    }

2.2.1 span原理

std::span 有两个模板参数:

cpp 复制代码
std::span<T, Extent>

std::dynamic_extent (默认): 长度在运行时确定。这是最常用的形式,如 std::span。

静态长度: 长度在编译时确定。如 std::span<int, 5>。这允许编译器进行更激进的优化,并强制长度检查。

2.2.2 切片功能

std::span 最强大的功能之一是能够轻松创建子视图,而无需复制数据。这类似于 Python 的切片或 Go 的 slice。

cpp 复制代码
    void test()
    {
        std::vector<int> data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        std::span<int> s = data;

        // 获取前3个元素
		std::span<int> first3 = s.first<3>();
        for (int x : first3)
        {
            std::cout << x << " ";
        }
        cout << endl;

        // 获取后3个元素
		std::span<int> last3 = s.last<3>();
        for (int x : last3)
        {
            std::cout << x << " ";
        }
        cout << endl;

		// 获取从索引2开始的4个元素
        std::span<int> subspan = s.subspan(2, 4);
        for (int x : subspan)
        {
            std::cout << x << " ";
        }
        cout << endl;
        /*
            0 1 2 
            7 8 9 
            2 3 4 5 
		*/
    }

2.2.3 读取字节

std::as_bytes 和 std::as_writable_bytes 可以将任何 span 转换为字节视图(std::span),这在序列化或网络编程中非常有用。

cpp 复制代码
void send_data(std::span<const std::byte> buffer) {
    // 发送 buffer.size() 字节...
}

int main() {
    double values[] = {1.1, 2.2};
    // 自动将 double 数组视为字节序列
    send_data(std::as_bytes(std::span(values))); 
}

2.2.4 悬空引用

因为 std::span 不拥有内存,所以必须确保 span 的生命周期不超过它指向的数据的生命周期。

cpp 复制代码
std::span<int> get_dangling() {
    std::vector<int> v = {1, 2, 3};
    return v; //  错误方式,v 在函数结束时被销毁,返回的 span 指向无效内存。
}

2.2.5 span总结

span优点:零拷贝,统一接口(连续内存都可以使用span处理),类型安全,支持切片。

需要注意的是,使用span时,避免悬空引用。

相关推荐
ULTRA??2 小时前
C++20模块( import 核心用法)
c++·c++20
Mr_WangAndy17 小时前
C++20新特性_概念 (Concepts)
c++20·c++20新特性·c++40周年·c++20概念
Mr_WangAndy17 小时前
C++20新特性_范围 (Ranges)
c++20·c++20新特性·c++40周年·范围ranges·视图适配器·视图view
Mr_WangAndy1 天前
C++20新特性_[[no_unique_address]]属性
c++20·c++20新特性·c++40周年
Mr_WangAndy1 天前
C++20新特性_模块(Modules)
c++20·c++40周年·c++20新特性模块
Mr_WangAndy1 天前
C++20新特性_范围 `for` 循环的初始化语句
c++20·c++40周年·范围for初始化
Mr_WangAndy1 天前
C++20新特性_三路比较运算符 (<=>)
c++20·c++40周年·三路比较运算符
Mr_WangAndy1 天前
C++20新特性_consteval 和 constinit
c++20·c++40周年·consteval·constinit
Azxcc01 天前
c++20协程浅析
网络·c++20