C++20新特性_std::format和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:{}用法说明。

    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:下面的示例中在{}中指定参数顺序。

    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支持丰富的格式化选项,语法如下:

{:[fill] [align] [sign] [#] [0] [width] [.precision] [type] }

{} : 替换字符的标志,其中包含要格式化的参数索引和格式说明。
“:” : 分割符,用于分隔参数索引和格式说明。
在这里插入图片描述
示例:下面测试每个符号的用法。

    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风格的数组。编写一个函数处理这个数组时,在函数中传递这个数组,如下:

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

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

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

    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 有两个模板参数:

std::span<T, Extent>

std::dynamic_extent (默认): 长度在运行时确定。这是最常用的形式,如 std::span。
静态长度: 长度在编译时确定。如 std::span<int, 5>。这允许编译器进行更激进的优化,并强制长度检查。

2.2.2 切片功能

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

    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),这在序列化或网络编程中非常有用。

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 的生命周期不超过它指向的数据的生命周期。

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

2.2.5 span总结

span优点:零拷贝,统一接口(连续内存都可以使用span处理),类型安全,支持切片。
需要注意的是,使用span时,避免悬空引用。

当__cplusplus == 202400 为啥cmd /c chcp 65001>nul && D:\mingw_msys2\ucrt64\bin\g++.exe -fdiagnostics-color=always -g D:\bicheng\zhzy\hello.cpp -o D:\bicheng\zhzy\hello.exe -std=c++26 -O2 D:/mingw_msys2/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\李逸扬\AppData\Local\Temp\ccQ0Og5z.o: in function `std::vprint_unicode(_iobuf*, std::basic_string_view<char, std::char_traits<char> >, std::basic_format_args<std::basic_format_context<std::__format::_Sink_iter<char>, char> >)': D:/mingw_msys2/ucrt64/include/c++/15.1.0/print:81:(.text$_ZSt14vprint_unicodeP6_iobufSt17basic_string_viewIcSt11char_traitsIcEESt17basic_format_argsISt20basic_format_contextINSt8__format10_Sink_iterIcEEcEE[_ZSt14vprint_unicodeP6_iobufSt17basic_string_viewIcSt11char_traitsIcEESt17basic_format_argsISt20basic_format_contextINSt8__format10_Sink_iterIcEEcEE]+0x1a2): undefined reference to `std::__open_terminal(_iobuf*)' D:/mingw_msys2/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: D:/mingw_msys2/ucrt64/include/c++/15.1.0/print:87:(.text$_ZSt14vprint_unicodeP6_iobufSt17basic_string_viewIcSt11char_traitsIcEESt17basic_format_argsISt20basic_format_contextINSt8__format10_Sink_iterIcEEcEE[_ZSt14vprint_unicodeP6_iobufSt17basic_string_viewIcSt11char_traitsIcEESt17basic_format_argsISt20basic_format_contextINSt8__format10_Sink_iterIcEEcEE]+0x3fc): undefined reference to `std::__write_to_terminal(void*, std::span<char, 18446744073709551615ull>)' collect2.exe: error: ld returned 1 exit status 请给出解决方案
08-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值