当前位置: 首页 > news >正文

现代C++(C++17/20)特性详解

现代C++的核心哲学是:更接近意图的表达(更简洁)、更强的类型安全(更安全)、零开销抽象(更高性能)、以及更好的并发支持


第一部分:C++17 核心特性

1. std::optional - 表示可选值
  • 传统方法:使用特殊值(如 -1, nullptr, std::string::npos)或一个 std::pair<T, bool>。这两种方式都不直观且容易出错。
  • 现代方法std::optional<T> 明确表达了一个值可能存在也可能不存在的语义。
  • 示例对比
    // 传统 - 返回-1表示找不到
    int find_index(const std::vector<std::string>& vec, const std::string& value) {auto it = std::find(vec.begin(), vec.end(), value);if (it != vec.end()) return std::distance(vec.begin(), it);return -1; // 魔数,不直观
    }
    // 调用者必须检查魔数
    int idx = find_index(vec, "key");
    if (idx != -1) { ... } // 容易忘记检查// 现代 - 使用 std::optional
    std::optional<size_t> find_index(const std::vector<std::string>& vec, const std::string& value) {auto it = std::find(vec.begin(), vec.end(), value);if (it != vec.end()) return std::distance(vec.begin(), it);return std::nullopt; // 明确表示无值
    }
    // 调用者代码清晰安全
    if (auto idx = find_index(vec, "key"); idx.has_value()) {std::cout << "Found at: " << idx.value() << std::endl;
    }
    
  • 应用场景:数据库查询可能为空的结果、解析用户输入(可能未提供)、查找算法、函数可能失败但不需要详细错误信息的场合。
2. std::variant - 类型安全的联合体
  • 传统方法:使用C风格union(无法安全持有非平凡类型)或继承体系(设计沉重,需要动态分配)。
  • 现代方法std::variant<Types...> 可以安全地持有多种预定义类型中的一种。
  • 示例对比
    // 传统 - 继承(冗长)
    struct Animal { virtual ~Animal() = default; };
    struct Dog : Animal { void bark(); };
    struct Cat : Animal { void meow(); };
    // 需要手动管理指针和动态转换// 现代 - 使用 std::variant
    #include <variant>
    struct Dog { void bark(); };
    struct Cat { void meow(); };
    using Animal = std::variant<Dog, Cat>;Animal animal = Dog{};
    // 使用 std::visit 来访问,类型安全
    std::visit([](auto& arg) {using T = std::decay_t<decltype(arg)>;if constexpr (std::is_same_v<T, Dog>) arg.bark();else if constexpr (std::is_same_v<T, Cat>) arg.meow();
    }, animal);
    
  • 应用场景:实现状态机、解析JSON/XML等结构化数据(节点可以是字符串、数字、数组等)、错误处理(返回std::variant<Result, ErrorCode>)。
3. std::string_view - 字符串的非拥有视图
  • 传统方法:使用const std::string&(可能导致不必要的构造)或const char*(需要手动管理长度,易出错)。
  • 现代方法std::string_view 包含一个指针和一个长度,低成本地“查看”一个字符串,而不拥有其内存。
  • 示例对比
    // 传统 - 传递 const std::string&,如果传入C字符串会触发构造
    void process_string(const std::string& str) { ... }
    process_string("Hello"); // 隐式转换,构造临时std::string对象// 传统 - 传递 const char*,丢失长度信息
    void process_c_string(const char* ptr) { ... } // 不安全,不知道多长// 现代 - 使用 std::string_view,无拷贝,有长度信息
    void process_string_sv(std::string_view sv) {std::cout << sv.substr(0, 5) << std::endl; // 子串操作极快
    }
    std::string s = "Hello World";
    process_string_sv(s); // 无拷贝
    process_string_sv("Hello"); // 无拷贝,从字面量构造string_view
    
  • 应用场景:函数参数、字符串分割和子串操作、解析器、日志处理,凡是只读访问字符串的地方都应优先考虑。
4. 结构化绑定 - 解包元组和结构体
  • 传统方法:使用std::tie或手动访问std::get<N>
  • 现代方法:一次性将元组或结构体的成员声明为变量。
  • 示例对比
    // 传统 - 使用 std::tie
    std::tuple<int, std::string, double> get_data();
    int id;
    std::string name;
    double value;
    std::tie(id, name, value) = get_data(); // 需要预先声明变量// 传统 - 使用 std::get
    auto data = get_data();
    int id = std::get<0>(data);
    std::string name = std::get<1>(data);// 现代 - 结构化绑定
    auto [id, name, value] = get_data(); // 简洁明了,一次性声明所有变量// 同样适用于结构体
    struct Point { int x; int y; };
    Point p{10, 20};
    auto [x, y] = p; // x = 10, y = 20
    
  • 应用场景:遍历std::mapfor (const auto& [key, value] : my_map))、处理多返回值函数、简化代码。

第二部分:C++20 核心特性

1. 概念 - 给模板参数加上约束
  • 传统方法:使用typename,错误信息晦涩难懂,通常在模板实例化深处报错。
  • 现代方法:使用concept定义约束,错误信息清晰,在调用处即可发现。
  • 示例对比
    // 传统 - 错误信息糟糕
    template<typename T>
    void sort_and_print(T& container) {std::sort(container.begin(), container.end());for (const auto& elem : container) { ... }
    }
    // 如果传入一个没有begin()的类型,错误信息会非常长且难以理解// 现代 - 使用概念
    #include <concepts>
    template<typename T>
    concept SortableContainer = requires(T cont) {requires std::random_access_iterator<typename T::iterator>; // 需要随机访问迭代器requires std::totally_ordered<typename T::value_type>; // 元素类型可比较
    };template<SortableContainer T> // 清晰的约束!
    void sort_and_print(T& container) {std::sort(container.begin(), container.end());for (const auto& elem : container) { ... }
    }struct NotSortable { int data; };
    std::list<NotSortable> my_list; // list不是随机访问,NotSortable不可比较
    // sort_and_print(my_list); // 编译错误在此行!非常清晰:
    // “错误: ‘SortableContainer’约束不被满足”
    // “注意: ‘std::list<NotSortable>’不满足‘SortableContainer’”
    
  • 应用场景:编写模板库(如STL)、为泛型接口定义清晰的契约、大幅改善开发体验。
2. 范围库 - 现代算法与视图
  • 传统方法:STL算法需要首尾迭代器,操作难以组合,产生中间变量。
  • 现代方法: ranges库提供管道操作符|,支持惰性求值的视图,可以组合操作。
  • 示例对比
    // 传统 - 繁琐,有中间变量
    std::vector<int> vec = {5, 3, 8, 1, 4, 2};
    std::sort(vec.begin(), vec.end()); // 原地排序
    std::vector<int> even_vec;
    std::copy_if(vec.begin(), vec.end(), std::back_inserter(even_vec),[](int x) { return x % 2 == 0; }); // 产生中间容器// 现代 - 使用Ranges(组合、惰性、无中间变量)
    #include <ranges>
    namespace vw = std::views;auto even_squares = vec| vw::filter([](int x) { return x % 2 == 0; }) // 过滤偶数| vw::transform([](int x) { return x * x; })   // 平方映射| vw::take(3);                                 // 取前3个
    // 以上操作是惰性的,尚未执行
    for (auto x : even_squares) { // 在此循环时才真正计算std::cout << x << " ";
    }
    
  • 应用场景:数据处理的管道式操作、流式处理、避免创建不必要的临时容器、编写声明式风格的代码。
3. std::span - 连续序列的视图
  • 传统方法:传递指针和大小 (T* ptr, size_t len),容易出错。
  • 现代方法std::span<T> 封装了指针和大小,提供安全的访问接口。
  • 示例对比
    // 传统 - 易出错
    void process_array(int* data, size_t size) {for (size_t i = 0; i < size; ++i) { ... }
    }
    // 调用者必须确保size正确
    int arr[100];
    process_array(arr, 100); // 容易传错size// 现代 - 安全方便
    #include <span>
    void process_span(std::span<int> data) {for (auto& elem : data) { ... } // 可像容器一样范围for循环if (!data.empty()) {data[0] = 1; // 带边界检查的访问(调试模式下)}
    }
    std::vector<int> vec(100);
    process_span(vec); // 自动推导大小!
    process_span(arr); // 同样可以处理C风格数组!
    
  • 应用场景:与C风格API交互、处理内存缓冲区、函数接收数组/向量切片作为参数。
4. 三路比较 <=> - 简化比较运算符定义
  • 传统方法:需要手动重载 ==, !=, <, <=, >, >= 共6个运算符,繁琐且易出错。
  • 现代方法:只需重载 <=>(和 ==(C++20)),编译器自动生成所有其他比较运算符。
  • 示例对比
    // 传统 - 冗长
    struct Point {int x, y;bool operator==(const Point& other) const { return x == other.x && y == other.y; }bool operator!=(const Point& other) const { return !(*this == other); }bool operator<(const Point& other) const { return x < other.x || (x == other.x && y < other.y); }// ... 还需要实现 >, <=, >=,极其繁琐
    };// 现代 - 简洁
    #include <compare>
    struct Point {int x, y;// 编译器自动生成 !=, <, <=, >, >=auto operator<=>(const Point& other) const = default;
    };
    // 或者自定义比较逻辑
    struct CaseInsensitiveString {std::string s;// 自定义三路比较逻辑std::strong_ordering operator<=>(const CaseInsensitiveString& b) const {return case_insensitive_compare(s.c_str(), b.s.c_str());}bool operator==(const CaseInsensitiveString& b) const { return (*this <=> b) == 0; }
    };
    
  • 应用场景:任何需要定义比较逻辑的自定义数据结构,极大地减少了样板代码。

总结

特性解决的传统痛点现代C++方案的优势
std::optional魔数、冗余的pair<T, bool>表达意图清晰、类型安全
std::variant不安全的union、沉重的继承类型安全、可访问复杂类型
std::string_viewconst string&的构造开销、const char*的不安全零开销、安全、高性能
结构化绑定std::tie的冗长、std::get的晦涩代码简洁、可读性极高
概念模板错误信息晦涩、接口约束不明确错误信息清晰、接口自文档化
范围库迭代器配对繁琐、操作无法组合、产生中间变量声明式编程、惰性求值、性能更好
std::span指针+大小参数易错、不安全安全、方便、统一接口
<=>重复定义多个比较运算符、易出错极大简化代码、避免错误

参考

现代 C++ 特性(C++11/14/17/20)

完美转发(std::forward)详解:保留参数原始类型


文章转载自:

http://FJFIYCN8.wpwyx.cn
http://KuvUSqiA.wpwyx.cn
http://LyQnivny.wpwyx.cn
http://hilvBWZY.wpwyx.cn
http://n6EyEZ2Z.wpwyx.cn
http://NHQkF3dy.wpwyx.cn
http://o6gJzm3H.wpwyx.cn
http://KThzJOiu.wpwyx.cn
http://FPKI1vnk.wpwyx.cn
http://izSoVCLa.wpwyx.cn
http://uc9haKVZ.wpwyx.cn
http://NloiHw1D.wpwyx.cn
http://YoIuZeYY.wpwyx.cn
http://PmVlCMJ8.wpwyx.cn
http://iEmpgvot.wpwyx.cn
http://nB0csxeM.wpwyx.cn
http://iW1WibMd.wpwyx.cn
http://TkDXZ13u.wpwyx.cn
http://R0pckPVL.wpwyx.cn
http://A9Jaokxr.wpwyx.cn
http://32eRPrjE.wpwyx.cn
http://VkVnAS0r.wpwyx.cn
http://fqMgNwUS.wpwyx.cn
http://v5wreMwl.wpwyx.cn
http://8jhqlyni.wpwyx.cn
http://oTshbMeT.wpwyx.cn
http://eIybNnE7.wpwyx.cn
http://H7lYWEkv.wpwyx.cn
http://c3b8ONEB.wpwyx.cn
http://IfSkcL8Z.wpwyx.cn
http://www.dtcms.com/a/371774.html

相关文章:

  • 【C++】继承机制:面向对象编程的核心奥秘
  • 深度学习周报(9.1~9.7)
  • Spring 日志文件
  • 【HARP 第二期】HARP 的数据组织“约定”规范
  • 钾元素:从基础认知到多元应用与前沿探索
  • 如何短时间内精准定位指标异动根源
  • Geogebra 绘制 电磁波反射折射+斯涅尔定律+半波损失
  • Mia for Gmail for Mac 邮件管理软件
  • EXCEL VBA 清空Excel工作表(Sheet)的方法
  • kafka如何保证消息的顺序性
  • Python快速入门专业版(十):字符串特殊操作:去除空格、判断类型与编码转换
  • 【数据分析】微生物组数据的批次校正与分析
  • 技术前瞻:衡石Data Agent在多模态AI与复杂数据源下的扩展与挑战
  • 如何通过 Activepieces 实现智能工作流自动化
  • Knex 和 Schema 是什么?
  • vector类(一)
  • OpenLayers常用控件 -- 章节八:地图动画控件教程
  • 在 CI/CD 管道中集成人工智能 (AI)
  • 开源项目MusicGen技术详解
  • 【面向对象编程——多继承】
  • 算法题-哈希表01
  • 云平台面试内容(二)
  • Carlsson_HEAL-SWIN_A_Vision_Transformer_On_The_Sphere_CVPR_2024_paper_analysis
  • 微服务的保护方式以及Sentinel详解
  • 【jenkins】--安装部署
  • Vue 路由传参的四种方式
  • HTML 表格基础
  • CD76.【C++ Dev】AVL的模拟实现(1) 以左单旋为切口,分析旋转规律
  • 中国计算机发展史
  • LeetCode刷题记录----20.有效的括号(Easy)