【GiraKoo】C++17的新特性

C++17 被认为是自 C++11 以来最重要的版本,它并非一场革命,而是一次全面的"生活质量"提升。此版本专注于简化日常编码、增强语言表达力、提高代码安全性与性能,为开发者带来了大量实用工具,并为 C++20 的重大变革奠定了坚实基础。 本文将深入探讨 C++17 的核心特性,通过丰富的代码示例和实践场景,助您全面掌握这一现代 C++ 的关键版本。

  • C++17的新特性
    • 核心语言特性增强
      • [1. 结构化绑定 (Structured Bindings)](#1. 结构化绑定 (Structured Bindings) "#1-%E7%BB%93%E6%9E%84%E5%8C%96%E7%BB%91%E5%AE%9A-structured-bindings")
      • [2. if 和 switch 初始化语句](#2. if 和 switch 初始化语句 "#2-if-%E5%92%8C-switch-%E5%88%9D%E5%A7%8B%E5%8C%96%E8%AF%AD%E5%8F%A5")
      • [3. 内联变量 (Inline Variables)](#3. 内联变量 (Inline Variables) "#3-%E5%86%85%E8%81%94%E5%8F%98%E9%87%8F-inline-variables")
      • [4. 折叠表达式 (Fold Expressions)](#4. 折叠表达式 (Fold Expressions) "#4-%E6%8A%98%E5%8F%A0%E8%A1%A8%E8%BE%BE%E5%BC%8F-fold-expressions")
      • [5. constexpr lambda 表达式](#5. constexpr lambda 表达式 "#5-constexpr-lambda-%E8%A1%A8%E8%BE%BE%E5%BC%8F")
      • [6. 类模板参数推导 (CTAD - Class Template Argument Deduction)](#6. 类模板参数推导 (CTAD - Class Template Argument Deduction) "#6-%E7%B1%BB%E6%A8%A1%E6%9D%BF%E5%8F%82%E6%95%B0%E6%8E%A8%E5%AF%BC-ctad---class-template-argument-deduction")
      • [7. std::optional](#7. std::optional "#7-stdoptional")
      • [8. std::variant](#8. std::variant "#8-stdvariant")
      • [9. std::string_view](#9. std::string_view "#9-stdstring_view")
      • [10. std::filesystem](#10. std::filesystem "#10-stdfilesystem")
      • [11. 嵌套命名空间 (Nested Namespaces)](#11. 嵌套命名空间 (Nested Namespaces) "#11-%E5%B5%8C%E5%A5%97%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4-nested-namespaces")
      • [12. if constexpr](#12. if constexpr "#12-if-constexpr")
      • [13. 属性扩展 (Attribute Extensions)](#13. 属性扩展 (Attribute Extensions) "#13-%E5%B1%9E%E6%80%A7%E6%89%A9%E5%B1%95-attribute-extensions")
    • 总结

核心语言特性增强

1. 结构化绑定 (Structured Bindings)

允许使用 auto 关键字一次性声明多个变量,并从元组、结构体或数组中直接解构赋值,极大提升了代码的可读性和简洁性。

C++17 之前

cpp 复制代码
#include <tuple>
#include <iostream>

std::tuple<int, std::string, double> get_student() {
    return {1, "Alice", 3.8};
}

int main() {
    auto student = get_student();
    int id = std::get<0>(student);
    std::string name = std::get<1>(student);
    double gpa = std::get<2>(student);
    std::cout << "ID: " << id << ", Name: " << name << ", GPA: " << gpa << '\n';
}

使用 C++17 结构化绑定

cpp 复制代码
#include <iostream>
#include <tuple>
#include <map>
#include <string>

struct Point { double x, y; };

int main() {
    //  解构元组 (Tuple)
    auto [id, name, gpa] = std::make_tuple(1, "Alice", 3.8);
    std::cout << "ID: " << id << ", Name: " << name << ", GPA: " << gpa << '\n';

    // 2. 解构结构体/类 (Struct/Class)
    Point p{3.5, 7.2};
    auto& [x_ref, y_ref] = p; // 可以绑定为引用,直接修改原对象
    x_ref = 4.0;
    std::cout << "Point: (" << p.x << ", " << p.y << ")\n"; // 输出 (4, 7.2)

    // 3. 解构原生数组 (C-style Array)
    int arr[] = {10, 20, 30};
    auto [a, b, c] = arr;
    std::cout << "Array: " << a << ", " << b << ", " << c << '\n';

    // 4. 在循环中解构 map 元素 (常用场景)
    std::map<std::string, int> scores{{"Bob", 85}, {"Carol", 92}};
    for (const auto& [student_name, score] : scores) {
        std::cout << student_name << " got " << score << " points.\n";
    }

    // 5. 结合 if 初始化语句与 map::try_emplace
    if (auto [iter, inserted] = scores.try_emplace("David", 95); inserted) {
        std::cout << "David inserted with score: " << iter->second << '\n';
    } else {
        std::cout << "David already exists with score: " << iter->second << '\n';
    }
}

2. if 和 switch 初始化语句

允许在 ifswitch 控制结构中直接声明和初始化变量。这个变量的作用域被严格限制在 if-elseswitch 块内,有效避免了变量作用域泄漏和命名冲突。

C++17 之前

cpp 复制代码
#include <mutex>

std::mutex mtx;

void legacy_func() {
    mtx.lock(); // 必须手动管理锁
    // ... 临界区代码 ...
    // 如果这里发生异常或提前返回,unlock 可能不会被调用
    mtx.unlock();
}

// 稍好的做法,但 lock_guard 变量作用域仍然过大
void slightly_better() {
    std::lock_guard<std::mutex> guard(mtx);
    // ... 临界区代码 ...
} // guard 在函数结束时解锁

使用 C++17 if 初始化语句

cpp 复制代码
#include <iostream>
#include <fstream>
#include <cctype>
#include <mutex>

std::mutex file_mutex;

int main() {
    //  if 初始化:变量 `file` 的作用域仅限于 if-else 块
    if (std::ifstream file("data.txt"); file.is_open()) {
        std::cout << "File opened successfully.\n";
        // file 在此可见
    } else {
        std::cout << "Failed to open file.\n";
        // file 在此也可见
    }
    // file 在此已销毁,无法访问

    // 2. switch 初始化:变量 `c` 的作用域仅限于 switch 块
    switch (char c = static_cast<char>(std::getchar()); std::tolower(c)) {
        case 'y': 
            std::cout << "You chose yes.\n"; 
            break;
        case 'n': 
            std::cout << "You chose no.\n"; 
            break;
        default:  
            std::cout << "Invalid choice. You entered: " << c << '\n';
    }

    // 3. 结合 lock_guard 实现更精细的锁作用域
    if (std::lock_guard<std::mutex> lock(file_mutex); true) {
        std::cout << "Critical section is locked.\n";
        // ... 执行受保护的操作 ...
    } // lock 在 if 语句结束时自动解锁
    std::cout << "Critical section is now unlocked.\n";
}

3. 内联变量 (Inline Variables)

解决了 C++17 之前在头文件中初始化静态成员变量的"ODR-use"(单一定义规则使用)问题。使用 inline 关键字,可以在头文件中直接定义和初始化静态成员变量,而不会在多个翻译单元中引发链接错误。

C++17 之前 (mylib.h)

cpp 复制代码
// mylib.h
#pragma once

struct Constants {
    static const int MAX_BUFFER_SIZE; // 声明
};

// mylib.cpp
#include "mylib.h"
const int Constants::MAX_BUFFER_SIZE = 1024; // 定义和初始化必须在源文件中

使用 C++17 内联变量 (mylib.h)

cpp 复制代码
// mylib.h
#pragma once
#include <string>

//  内联全局常量
inline constexpr double PI = 3.1415926535;

// 2. 内联类静态成员变量
struct AppConfig {
    static inline int version_major = 1;
    static inline std::string app_name = "MyApp";
};

// main.cpp
#include <iostream>
#include "mylib.h"

int main() {
    std::cout << "PI: " << PI << '\n';
    std::cout << "App Name: " << AppConfig::app_name << '\n';
    std::cout << "Version: " << AppConfig::version_major << '\n';

    // 可以在任何包含头文件的地方修改
    AppConfig::app_name = "MyAwesomeApp";
    std::cout << "New App Name: " << AppConfig::app_name << '\n';
}

4. 折叠表达式 (Fold Expressions)

极大地简化了对参数包的操作,使得编写可变参数模板函数(如求和、打印所有参数等)变得异常简洁和直观。

C++17 之前 (使用递归)

cpp 复制代码
#include <iostream>

// 递归终止条件
long long sum_recursive() {
    return 0;
}

// 递归模板
template<typename T, typename... Args>
long long sum_recursive(T first, Args... rest) {
    return first + sum_recursive(rest...);
}

使用 C++17 折叠表达式

cpp 复制代码
#include <iostream>
#include <type_traits>

//  一元右折叠 (Unary Right Fold)
template<typename... Args>
auto sum(Args... args) {
    // (arg1 + (arg2 + (arg3 + ...)))
    return (args + ...);
}

// 2. 一元左折叠 (Unary Left Fold)
template<typename... Args>
void print_left(Args... args) {
    // (((std::cout << arg1) << arg2) << ...)
    (std::cout << ... << args) << '\n';
}

// 3. 二元右折叠 (Binary Right Fold)
template<typename... Args>
auto subtract_from(int initial, Args... args) {
    // (initial - (arg1 - (arg2 - ...)))
    return (initial - ... - args);
}

// 4. 编译期检查所有类型是否相同
template<typename T, typename... Args>
constexpr bool all_same() {
    return (std::is_same_v<T, Args> && ...);
}

int main() {
    std::cout << "Sum: " << sum(1, 2, 3, 4.5, 5) << '\n';
    
    std::cout << "Printing elements: ";
    print_left("Hello", ", ", 2024, '!');

    std::cout << "Binary fold: " << subtract_from(100, 10, 5) << '\n'; // 100 - (10 - 5) = 95

    static_assert(all_same<int, int, int>(), "All types must be int");
    // static_assert(all_same<int, double, int>(), "This will fail");
    std::cout << "Compile-time type check passed.\n";
}

5. constexpr lambda 表达式

允许 lambda 表达式在编译期求值。这使得原本需要在运行时计算的逻辑可以提前到编译阶段完成,从而提升运行时性能,并能用于更多编译期元编程场景。

C++17 之前:lambda 表达式只能在运行时执行。

使用 C++17 constexpr lambda

cpp 复制代码
#include <iostream>
#include <array>

// 编译期计算阶乘的 lambda
constexpr auto factorial = [](int n) {
    long long result = 1;
    for (int i = 1; i <= n; ++i) {
        result *= i;
    }
    return result;
};

// 编译期创建并填充数组的 lambda
constexpr auto create_powers_of_two(size_t N) {
    return [N]() {
        std::array<int, N> arr{};
        for (size_t i = 0; i < N; ++i) {
            arr[i] = 1 << i;
        }
        return arr;
    }();
}

int main() {
    //  在编译期计算常量
    constexpr long long fact5 = factorial(5);
    static_assert(fact5 == 120, "Factorial calculation failed at compile time");
    std::cout << "5! = " << fact5 << " (calculated at compile time)\n";

    // 2. 在编译期生成查找表
    constexpr auto powers = create_powers_of_two(8);
    static_assert(powers[3] == 8, "Array initialization failed at compile time");

    std::cout << "Powers of two (generated at compile time):\n";
    for (size_t i = 0; i < powers.size(); ++i) {
        std::cout << "2^" << i << " = " << powers[i] << '\n';
    }
}

6. 类模板参数推导 (CTAD - Class Template Argument Deduction)

允许编译器根据构造函数参数自动推导类模板的模板参数,使得创建模板类对象时无需显式指定类型,语法更简洁,类似于普通类的实例化。

C++17 之前

cpp 复制代码
#include <vector>
#include <utility>
#include <mutex>
#include <thread>

// 必须显式指定模板参数
std::vector<int> numbers = {1, 2, 3};
std::pair<int, std::string> person(1, "Alice");
std::lock_guard<std::mutex> lock(my_mutex);

使用 C++17 CTAD

cpp 复制代码
#include <vector>
#include <utility>
#include <string>
#include <mutex>
#include <iostream>

// 自定义模板类
template<typename T1, typename T2>
struct MyPair {
    T1 first;
    T2 second;
    MyPair(T1 f, T2 s) : first(f), second(s) {}
};

// 推导指引 (Deduction Guide) - 可选,用于复杂情况
template<typename T>
MyPair(T, const char*) -> MyPair<T, std::string>;

int main() {
    //  标准库中的 CTAD
    std::vector v = {1, 2, 3, 4}; // 自动推导为 std::vector<int>
    std::pair p(101, "Bob");       // 自动推导为 std::pair<int, const char*>
    
    std::mutex mtx;
    std::lock_guard lock(mtx); // 自动推导为 std::lock_guard<std::mutex>

    // 2. 自定义模板类的 CTAD
    MyPair mp1(1, 3.14); // 推导为 MyPair<int, double>
    MyPair mp2(2, "hello"); // 使用推导指引,推导为 MyPair<int, std::string>

    std::cout << "Vector type: " << typeid(v).name() << '\n';
    std::cout << "Pair type: " << typeid(p).name() << '\n';
    std::cout << "MyPair1 type: " << typeid(mp1).name() << '\n';
    std::cout << "MyPair2 type: " << typeid(mp2).name() << '\n';
}

7. std::optional

提供了一种类型安全的方式来表示一个"可能包含值"或"不包含值"的对象,避免了使用裸指针、魔术数字(如 -1)或抛出异常来表示缺失值的情况。

C++17 之前 (常见做法)

cpp 复制代码
#include <string>

// 使用特殊值 -1 表示未找到
int find_user_id_legacy(const std::string& username) {
    if (username == "admin") return 0;
    return -1; // -1 是一个"魔术数字"
}

// 使用指针,可能引入空指针风险
int* find_user_id_pointer(const std::string& username) {
    if (username == "admin") {
        static int id = 0;
        return &id;
    }
    return nullptr;
}

使用 C++17 std::optional

cpp 复制代码
#include <iostream>
#include <optional>
#include <string>
#include <map>

std::map<std::string, int> user_database = {{"Alice", 101}, {"Bob", 102}};

// 返回一个 optional<int>,清晰地表达了"可能没有"的语义
std::optional<int> find_user_id(const std::string& username) {
    if (auto it = user_database.find(username); it != user_database.end()) {
        return it->second; // 返回包含值的 optional
    }
    return std::nullopt; // 返回空的 optional
}

int main() {
    //  查找存在的用户
    if (auto id_opt = find_user_id("Alice"); id_opt.has_value()) {
        std::cout << "Alice's ID: " << id_opt.value() << '\n';
    }

    // 2. 查找不存在的用户
    auto guest_id_opt = find_user_id("Guest");
    if (!guest_id_opt) {
        std::cout << "Guest user not found.\n";
    }

    // 3. 使用 value_or 提供默认值
    int bob_id = find_user_id("Bob").value_or(0);
    int charlie_id = find_user_id("Charlie").value_or(-1);
    std::cout << "Bob's ID (with default): " << bob_id << '\n';
    std::cout << "Charlie's ID (with default): " << charlie_id << '\n';
}

8. std::variant

提供了一个类型安全的、可区分的联合体(discriminated union)。它可以在一个对象中存储不同类型的值,但任何时候只持有一种类型,并能安全地查询和访问当前存储的类型。

C++17 之前 (使用 unionenum)

cpp 复制代码
#include <iostream>
#include <string>

enum class Type { Int, String };

struct OldVariant {
    Type type;
    union {
        int i;
        std::string s; // 错误:union 不能包含非 POD 类型
    } data; // 必须手动管理构造和析构
};

使用 C++17 std::variant

cpp 复制代码
#include <iostream>
#include <variant>
#include <string>
#include <vector>

// 定义一个可以持有不同网络消息类型的 variant
using NetworkMessage = std::variant<int, std::string, std::vector<char>>;

void process_message(const NetworkMessage& msg) {
    std::cout << "Processing message: ";
    std::visit([](const auto& value) {
        using T = std::decay_t<decltype(value)>;
        if constexpr (std::is_same_v<T, int>) {
            std::cout << "Heartbeat signal: " << value << '\n';
        } else if constexpr (std::is_same_v<T, std::string>) {
            std::cout << "Text message: '" << value << "'\n";
        } else if constexpr (std::is_same_v<T, std::vector<char>>) {
            std::cout << "Binary data of size: " << value.size() << " bytes\n";
        }
    }, msg);
}

int main() {
    NetworkMessage msg1 = 404;
    NetworkMessage msg2 = "Hello, server!";
    NetworkMessage msg3 = std::vector<char>{0xDE, 0xAD, 0xBE, 0xEF};

    process_message(msg1);
    process_message(msg2);
    process_message(msg3);

    // 查询当前类型
    if (std::holds_alternative<std::string>(msg2)) {
        std::cout << "Message 2 is a string: " << std::get<std::string>(msg2) << '\n';
    }
}

9. std::string_view

提供了一个对字符串的非拥有(non-owning)引用。它像指针和长度一样工作,允许你查看和操作字符串数据,而无需进行昂贵的内存分配或拷贝。特别适用于函数参数传递和子字符串操作。

C++17 之前 (使用 const std::string&const char*)

cpp 复制代码
#include <string>

// 每次调用都会创建一个新的 std::string 子串,导致内存分配
std::string get_first_word_legacy(const std::string& s) {
    size_t pos = s.find(' ');
    return s.substr(0, pos);
}

使用 C++17 std::string_view

cpp 复制代码
#include <iostream>
#include <string_view>

// 函数接受 string_view,避免了不必要的拷贝
void print_header(std::string_view header) {
    std::cout << "[Header] " << header << " [End Header]\n";
}

// 返回 string_view,无任何内存分配
std::string_view get_username(std::string_view url) {
    size_t start = url.find("user=");
    if (start == std::string_view::npos) return {};
    start += 5;
    
    size_t end = url.find('&', start);
    return url.substr(start, end - start);
}

int main() {
    std::string full_url = "https://example.com?user=Alice&session=12345";
    const char* c_style_url = "ftp://test.net?user=Bob&token=xyz";

    //  传递不同类型的字符串,无拷贝
    print_header("Content-Type: application/json");
    print_header(full_url);

    // 2. 高效提取子串
    std::string_view user_alice = get_username(full_url);
    std::string_view user_bob = get_username(c_style_url);

    std::cout << "Username from std::string: " << user_alice << '\n';
    std::cout << "Username from const char*: " << user_bob << '\n';

    // 3. 修改 string_view (不修改原始字符串)
    user_alice.remove_prefix(2);
    std::cout << "Trimmed username: " << user_alice << '\n';
    std::cout << "Original URL is unchanged: " << full_url << '\n';
}

10. std::filesystem

提供了一套标准的、跨平台的工具,用于操作文件和目录。在 C++17 之前,这些操作通常需要依赖特定于操作系统的 API(如 POSIX 的 <unistd.h> 或 Windows 的 <windows.h>)或第三方库(如 Boost.Filesystem)。

C++17 之前 (示例:使用 POSIX API)

cpp 复制代码
#include <unistd.h>
#include <sys/stat.h>

void create_dir_legacy() {
    mkdir("legacy_dir", 0755); // 平台相关
}

使用 C++17 std::filesystem

cpp 复制代码
#include <iostream>
#include <filesystem>
#include <fstream>

namespace fs = std::filesystem;

int main() {
    //  路径构建与操作
    fs::path base_dir = "/tmp/cpp17_demo";
    fs::path sub_dir = base_dir / "sub";
    fs::path file_path = sub_dir / "file.txt";

    std::cout << "Full path: " << file_path << '\n';
    std::cout << "Filename: " << file_path.filename() << '\n';
    std::cout << "Extension: " << file_path.extension() << '\n';

    try {
        // 2. 创建和删除目录
        fs::create_directories(sub_dir);
        std::cout << "Created directory: " << sub_dir << '\n';

        // 3. 创建文件
        std::ofstream(file_path) << "Hello, filesystem!";

        // 4. 检查文件状态
        if (fs::exists(file_path) && fs::is_regular_file(file_path)) {
            std::cout << "File size: " << fs::file_size(file_path) << " bytes\n";
        }

        // 5. 遍历目录
        std::cout << "\nListing contents of " << base_dir << ":\n";
        for (const auto& entry : fs::directory_iterator(base_dir)) {
            std::cout << entry.path() << (entry.is_directory() ? " [DIR]" : " [FILE]") << '\n';
        }

        // 清理
        // fs::remove_all(base_dir);
        // std::cout << "\nCleaned up directory: " << base_dir << '\n';

    } catch (const fs::filesystem_error& e) {
        std::cerr << "Filesystem error: " << e.what() << '\n';
    }
}

11. 嵌套命名空间 (Nested Namespaces)

提供了一种更简洁的语法来定义嵌套的命名空间,减少了代码的冗余和缩进层级,提高了可读性。

C++17 之前

cpp 复制代码
namespace MyLib {
    namespace Core {
        namespace Utils {
            void helper_function() { /* ... */ }
        }
    }
}

使用 C++17 嵌套命名空间

cpp 复制代码
#include <iostream>

// 使用 :: 分隔符一次性定义嵌套命名空间
namespace MyOrg::Networking::Protocols {
    void send_http_request() {
        std::cout << "Sending HTTP request via MyOrg::Networking::Protocols...\n";
    }
}

int main() {
    // 调用嵌套命名空间中的函数
    MyOrg::Networking::Protocols::send_http_request();
}

12. if constexpr

在编译期进行条件判断。如果条件为 false,则 if constexpr 块内的代码将被完全丢弃,不会进行语法分析或模板实例化。这解决了传统 if 语句在模板元编程中可能导致编译错误的痛点。

C++17 之前 (使用 SFINAE 或模板特化)

cpp 复制代码
#include <string>
#include <type_traits>

// SFINAE 版本
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
std::string to_string_legacy(T value) {
    return std::to_string(value);
}

template<typename T, std::enable_if_t<!std::is_arithmetic_v<T>, int> = 0>
std::string to_string_legacy(T value) {
    return std::string(value);
}

使用 C++17 if constexpr

cpp 复制代码
#include <iostream>
#include <string>
#include <type_traits>

// 一个函数处理所有情况,更清晰
template<typename T>
std::string to_string_universal(T value) {
    if constexpr (std::is_pointer_v<T>) {
        // 如果 T 是指针,解引用后再转换
        return to_string_universal(*value);
    } else if constexpr (std::is_arithmetic_v<T>) {
        // 如果 T 是算术类型,使用 std::to_string
        return std::to_string(value);
    } else {
        // 否则,假定它可以隐式转换为 string
        return std::string(value);
    }
}

int main() {
    int x = 42;
    int* p = &x;
    const char* s = "hello";

    std::cout << to_string_universal(x) << '\n';
    std::cout << to_string_universal(p) << '\n';
    std::cout << to_string_universal(s) << '\n';
}

13. 属性扩展 (Attribute Extensions)

C++17 引入或标准化了几个有用的属性,以增强代码的静态分析和可读性。

cpp 复制代码
#include <iostream>
#include <vector>

//  [[nodiscard]]: 提示编译器,函数的返回值不应被忽略。
// 常用于工厂函数或返回错误码的函数。
[[nodiscard]] bool connect_to_database(const std::string& host) {
    if (host.empty()) return false;
    // ... 连接逻辑 ...
    return true;
}

void setup_connection() {
    // 如果不使用返回值,编译器会发出警告
    connect_to_database("localhost"); // 警告: ignoring return value of function declared with 'nodiscard' attribute
    
    if (!connect_to_database("localhost")) {
        std::cerr << "Connection failed!\n";
    }
}

int main() {
    // 2. [[maybe_unused]]: 向编译器表明一个变量可能未被使用,以抑制警告。
    // 常用于回调函数中未使用的参数或功能开关控制的变量。
    int [[maybe_unused]] debug_level = 0;
    #ifdef ENABLE_DEBUG
        debug_level = 1;
    #endif

    // 3. [[fallthrough]]: 明确告知编译器,switch case 的穿透是故意的。
    char command = 'a';
    switch (command) {
        case 'a': // admin
            std::cout << "Admin access granted.\n";
            [[fallthrough]];
        case 'u': // user
            std::cout << "User access granted.\n";
            break;
        default:
            std::cout << "Guest access.\n";
    }
    
    setup_connection();
}

总结

C++17 是一次重大的版本迭代,其核心价值在于提升代码的现代化水平、可读性和安全性。它通过引入一系列"质量改进"特性,使得开发者能用更少的代码、更清晰的逻辑来表达复杂的意图。

核心影响

  • 代码更简洁 :结构化绑定、if/switch 初始化、内联变量和折叠表达式等特性,显著减少了样板代码。
  • 类型安全std::optionalstd::variantif constexpr 提供了更强大的类型安全保障,减少了运行时错误。
  • 性能提升std::string_viewconstexpr lambda 等特性避免了不必要的开销,提升了程序性能。
  • 标准库更强大std::filesystem 的加入填补了标准库在文件系统操作上的空白。

最佳实践建议拥抱结构化绑定 :在处理 pairtuplemap 等返回多个值的场景时,优先使用结构化绑定。 2. 善用 if constexpr :在模板元编程中,用 if constexpr 替代复杂的 SFINAE 和标签分发技术。 3. 传递 string_view :对于只读的字符串参数,优先使用 std::string_view,避免不必要的内存分配。 4. optionalvariant 建模 :当函数可能不返回值或返回多种类型时,使用 std::optionalstd::variant 来清晰地表达这种不确定性。 5. 利用属性 :为关键函数加上 [[nodiscard]],为意图明确的代码加上 [[maybe_unused]][[fallthrough]],让编译器成为你的代码审查伙伴。

注意:运行示例代码需要支持 C++17 的编译器(GCC 7+、Clang 5+、MSVC 19.15+)

相关推荐
xiaolang_8616_wjl5 小时前
c++文字游戏_闯关打怪2.0(开源)
开发语言·c++·开源
夜月yeyue5 小时前
设计模式分析
linux·c++·stm32·单片机·嵌入式硬件
无小道5 小时前
c++-引用(包括完美转发,移动构造,万能引用)
c语言·开发语言·汇编·c++
FirstFrost --sy7 小时前
数据结构之二叉树
c语言·数据结构·c++·算法·链表·深度优先·广度优先
Tanecious.7 小时前
C++--map和set的使用
开发语言·c++
Yingye Zhu(HPXXZYY)8 小时前
Codeforces 2021 C Those Who Are With Us
数据结构·c++·算法
liulilittle9 小时前
LinkedList 链表数据结构实现 (OPENPPP2)
开发语言·数据结构·c++·链表
无聊的小坏坏9 小时前
三种方法详解最长回文子串问题
c++·算法·回文串
山河木马9 小时前
前端学习C++之:.h(.hpp)与.cpp文件
前端·javascript·c++
2401_891957319 小时前
list的一些特性(C++)
开发语言·c++