C++23:多维视图(std::mdspan)

C++23:多维视图(std::mdspan)

介绍

在 C++23 中,std::mdspan 是一个非拥有的多维视图,用于表示连续对象序列。这个连续对象序列可以是一个简单的 C 数组、带有大小的指针、std::arraystd::vectorstd::string

这种多维视图通常被称为多维数组。

多维数组的形状由其维数(也称为秩)和每个维度的大小(也称为扩展)决定。std::mdspan 的大小是所有非零维度的大小的乘积。你可以使用多维索引运算符 [] 来访问 std::mdspan 的元素。

每个 std::mdspan 的维度可以有静态或动态的扩展。静态扩展意味着其长度在编译时指定;动态扩展意味着其长度在运行时指定。

定义

template<
    class T,
    class Extents,
    class LayoutPolicy = std::layout_right,
    class AccessorPolicy = std::default_accessor<T>
> class mdspan;
  • T: 连续对象序列的类型。
  • Extents: 指定维数及其大小;每个维度可以有静态或动态的扩展。
  • LayoutPolicy: 指定用于访问底层内存的布局策略。
  • AccessorPolicy: 指定如何引用底层元素。

由于 C++17 中的类模板参数推导(CTAD),编译器通常可以自动从初始化器的类型推导出模板参数。

示例

使用动态扩展

#include <mdspan>
#include <iostream>
#include <vector>

int main() {
    
    std::vector myVec{1, 2, 3, 4, 5, 6, 7, 8};          // (1)

    std::mdspan m{myVec.data(), 2, 4};                  // (2)
    std::cout << "m.rank(): " << m.rank() << '\n';      // (4)

    for (std::size_t i = 0; i < m.extent(0); ++i) {     // (6)
        for (std::size_t j = 0; j < m.extent(1); ++j) { // (7)
            std::cout << m[i, j] << ' ';                // (8)
        }
        std::cout << '\n';
    }

    std::cout << '\n';

    std::mdspan m2{myVec.data(), 4, 2};                 // (3)
    std::cout << "m2.rank(): " << m2.rank() << '\n';    // (5)

    for (std::size_t i = 0; i < m2.extent(0); ++i) {
        for (std::size_t j = 0; j < m2.extent(1); ++j) {
        std::cout << m2[i, j] << ' ';  
    }
    std::cout << '\n';
  }

}

使用静态和动态扩展

#include <mdspan>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>

int main() {
    
    std::vector myVec{1, 2, 3, 4, 5, 6, 7, 8};

    std::mdspan<int, std::extents<std::size_t, 2, 4>> m{myVec.data()}; // (1)
    std::cout << "m.rank(): " << m.rank() << '\n';

    for (std::size_t i = 0; i < m.extent(0); ++i) {
        for (std::size_t j = 0; j < m.extent(1); ++j) {
            std::cout << m[i, j] << ' ';  
        }
        std::cout << '\n';
    }

    std::cout << '\n';

    std::mdspan<int, std::extents<std::size_t, std::dynamic_extent, 
                std::dynamic_extent>> m2{myVec.data(), 4, 2};          // (2)
    std::cout << "m2.rank(): " << m2.rank() << '\n';

    for (std::size_t i = 0; i < m2.extent(0); ++i) {
        for (std::size_t j = 0; j < m2.extent(1); ++j) {
        std::cout << m2[i, j] << ' ';  
    }
    std::cout << '\n';
  }

   std::cout << '\n';

}

布局策略

std::mdspan 允许您指定用于访问底层内存的布局策略。默认情况下,使用 std::layout_right(C、C++ 或 Python 风格),但您也可以指定 std::layout_left(Fortran 或 MATLAB 风格)。

img

使用布局策略std::mdspanstd::layout_right遍历两个std::layout_left可以看出差异。

#include <mdspan>
#include <iostream>
#include <vector>

int main() {
    
    std::vector myVec{1, 2, 3, 4, 5, 6, 7, 8};

    std::mdspan<int, std::extents<std::size_t,      // (1)
         std::dynamic_extent, std::dynamic_extent>, 
         std::layout_right> m{myVec.data(), 4, 2};
    std::cout << "m.rank(): " << m.rank() << '\n';

    for (std::size_t i = 0; i < m.extent(0); ++i) {
        for (std::size_t j = 0; j < m.extent(1); ++j) {
            std::cout << m[i, j] << ' ';  
        }
        std::cout << '\n';
    }

    std::cout << '\n';

    std::mdspan<int, std::extents<std::size_t,     // (2)
         std::dynamic_extent, std::dynamic_extent>, 
         std::layout_left> m2{myVec.data(), 4, 2};
    std::cout << "m2.rank(): " << m2.rank() << '\n';

    for (std::size_t i = 0; i < m2.extent(0); ++i) {
        for (std::size_t j = 0; j < m2.extent(1); ++j) {
        std::cout << m2[i, j] << ' ';  
    }
    std::cout << '\n';
  }

}

img

接口概览

  • md[ind]: 访问第 ind 个元素。
  • md.size: 返回多维数组的大小。
  • md.rank: 返回多维数组的维数。
  • md.extents(i): 返回第 i 个维度的大小。
  • md.data_handle: 返回指向连续元素序列的指针。

参考:MC++ BLOG – Der Blog für Modernes C++ von Rainer Grimm (modernescpp.com)

<think>好的,我现在需要介绍C++23的新特性。首先,我得回想一下C++23的主要更新,确保信息准确。可能要从官方文档或可靠来源获取信息,比如C++标准委员会的文件或者权威的C++博客。 首先,C++23C++20之后的又一版本,属于中期更新,可能不会像C++11或C++20那样带来翻天覆地的变化,但仍有不少实用改进。我需要分门别类地整理这些特性,方便用户理解。 接下来,我需要考虑用户可能的背景。用户可能是有一定C++基础的开发者,想了解新版本有哪些改进,以便应用到项目中。因此,我需要用清晰的结构,将特性分成不同的类别,比如语言核心特性、标准库更新、其他改进等。 然后,逐一列举每个类别中的新特性。例如,在语言核心方面,C++23可能包括if consteval、auto(x)的语法、多维数组下标等。标准库方面,可能有std::mdspanstd::print、std::expected等新增内容。同时,可能还有一些现有功能的增强,比如STL容器的扩展。 需要注意,某些特性可能是在C++23中首次引入,而有些可能是在之前的版本中提出,但直到C++23才被正式纳入。需要确保这些信息的准确性,避免混淆。 另外,还要注意用中文表达,避免直接翻译术语带来的歧义。例如,正确翻译技术术语,同时保持语句通顺。例如,"if consteval"可以译为“if consteval 关键字”,但可能需要解释其用途。 在组织内容时,应该先概述C++23的整体定位,然后分点列出各个新特性,每个特性下简要说明其作用和用法。例如,std::print可能简化输出操作,替代传统的std::cout,这样用户能立刻理解其好处。 可能还需要比较C++23和之前版本的不同,比如在C++20中引入了概念(concepts),而C++23可能对此有增强。但需要确认是否确实有相关更新。 最后,需要检查是否有遗漏的重要特性,确保覆盖了用户可能关心的内容。同时,提醒用户这些特性可能需要最新的编译器支持,建议他们查看编译器文档以确认兼容性。 总结时,应强调C++23的改进如何提升开发效率和代码质量,比如简化语法、增强类型安全、优化性能等。让用户了解升级到C++23的实际好处。 现在需要将这些思考整理成结构清晰的回答,分点说明,每个部分用适当的标题,如语言特性、标准库、其他改进等,每个特性下给出简要解释,可能的话提供代码示例或应用场景。同时,保持语言简洁明了,避免过于技术化的术语堆砌,帮助用户逐步理解。</think>C++23C++20 后的重要更新,虽然未引入颠覆性变革,但提供了多项实用改进。以下是主要新特性的分类整理: --- ### 一、语言核心特性 1. **`if consteval` 关键字** 用于检测当前是否在常量求值上下文中,替代过去的 `std::is_constant_evaluated()`,简化编译时逻辑判断。 ```cpp constexpr int foo() { if consteval { return 1; } // 编译时返回1 else { return 0; } // 运行时返回0 } ``` 2. **多维数组下标支持** 允许通过 `a[i, j, k]` 语法访问多维数组,提升代码可读性(需自定义运算符重载支持)。 3. **`auto(x)` 语法(显式对象复制)** 生成对象的显式副本,避免隐式类型转换带来的歧义。 ```cpp std::string s = "hello"; auto copy = auto(s); // 显式生成副本 ``` 4. **类型推导增强** - `static_assert` 支持无消息参数:`static_assert(cond);` - 支持在 Lambda 捕获中推导模板参数。 --- ### 二、标准库新增内容 1. **`std::mdspan`(多维视图)** 提供对多维数组的非拥有引用,支持灵活的内存布局(如行优先、列优先),适用于科学计算。 ```cpp int data[2][3] = {{1,2,3}, {4,5,6}}; std::mdspan mat(data, 2, 3); // 2行3列视图 ``` 2. **格式化输出库 `std::print`** 简化输出语法,直接支持格式化字符串(需包含 `<print>` 头文件)。 ```cpp std::print("Hello {}! The answer is {}.", "World", 42); // 输出:Hello World! The answer is 42. ``` 3. **`std::expected`(期望值类型)** 提供更安全的错误处理机制,替代传统的错误码或异常,支持存储值或错误信息。 ```cpp std::expected<int, std::string> result = readFile(); if (result) { use(*result); } else { log_error(result.error()); } ``` 4. **`std::generator`(协程生成器)** 简化协程实现,支持生成惰性求值序列。 ```cpp std::generator<int> fib() { int a=0, b=1; while (true) { co_yield a; std::tie(a,b) = std::tuple{b, a+b}; } } ``` --- ### 三、现有功能增强 1. **STL 容器改进** - `std::vector` 支持 `constexpr`。 - `std::optional` 和 `std::variant` 新增 monadic 操作(如 `and_then()`、`transform()`)。 - `std::ranges` 扩展算法(如 `find_last`、`contains`)。 2. **模块化增强** 优化模块编译速度,支持模块分区中的 `using namespace`。 3. **网络库 TS 进展** 推进 `std::net` 的标准化,支持异步网络操作(可能延迟至 C++26)。 --- ### 四、其他改进 - **编译器资源管理器(Compiler Explorer)集成支持**:便于在线测试新特性。 - **性能优化**:如 `std::string::substr` 的复杂度从 $O(n)$ 优化为 $O(1)$(非复制操作)。 --- ### 五、注意事项 - **编译器支持**:需最新版本(如 GCC 13+、Clang 16+、MSVC 2022 17.5+)。 - **向后兼容性**:多数代码无需修改,但需注意废弃特性的替代方案。 C++23 通过简化语法、增强类型安全和优化性能,提升了开发效率。建议关注编译器更新,逐步尝试新特性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值