博客 阅读时间 10 分钟

C++26 即将到来,但自 C++11 以来,C++ 都增加了哪些重大特性?

分享本文
C++26 is coming, but what are the major features that have been added to C++ since C++11?

自 C++11 以来,现代 C++ 经历了一系列重大更新,每次更新都带来旨在让语言更高效、更可读、更易维护的新特性和增强。以下是自 C++11 以来各版本引入的主要特性概览,并附有对其使用情况的点评:

C++11

C++11 标志着 C++ 语言的重大演进,引入了多个强大特性,使 C++ 编程更加现代化和简化。以下是其中最具影响力的特性及说明其用法的示例:

1. auto 类型推导

auto关键字允许编译器根据变量的初始值自动推导其类型。

auto x = 42;       // int
auto y = 3.14;     // double
auto s = "hello";  // const char*

2.Lambda 表达式

Lambda 提供了一种在代码中直接定义匿名函数的简洁方式。

auto add = [](int a, int b) { return a + b; };
int result = add(3, 4);  // result is 7

3.基于范围的 for 循环

简化了对数组、vector 及其他容器等集合的遍历。

std::vector<int> numbers = {1, 2, 3, 4, 5};
for (int n : numbers) {
    std::cout << n << " ";
}

4.智能指针

引入std::unique_ptr和std::shared_ptr来自动管理动态内存,防止内存泄漏。

std::unique_ptr<int> ptr(new int(10));
// No need to delete ptr manually; it will be deleted when it goes out of scope

5.移动语义

移动语义通过允许资源转移而非拷贝来优化性能。

std::vector<int> makeVector() {
    std::vector<int> v = {1, 2, 3};
    return v;  // Moves v rather than copying
}

std::vector<int> v = makeVector();  // Efficient move

6.nullptr

用NULL替代nullptr,以更安全地表示空指针。

int* p = nullptr;  // p is a null pointer

7.constexpr

支持编译期常量表达式,通过在编译期执行计算来提升性能。

constexpr int square(int x) {
    return x * x;
}

int arr[square(5)];  // Creates an array of size 25

8.std::thread

提供创建和管理线程的标准方式,支持并发编程。

#include <thread>
#include <iostream>

void hello() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    std::thread t(hello);
    t.join();  // Wait for thread to finish
    return 0;
}

9.可变参数模板

支持参数数量可变的模板,使模板编程更加灵活。

template<typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << std::endl;  // Fold expression (C++17)
}

print(1, 2, "three", 4.0);

10.统一初始化

为变量和容器的初始化提供一致的语法。

int arr[] = {1, 2, 3};
std::vector<int> v = {1, 2, 3, 4, 5};
struct Point { int x, y; };
Point p = {1, 2};

C++14

C++14 在 C++11 的基础上,引入了若干重要增强,以提升代码的可读性、灵活性和性能。以下是主要特性及示例:

1. 泛型 lambda

C++14 允许在 lambda 参数列表中使用auto,使 lambda 更灵活、更易于与泛型代码配合使用。

示例:

auto add = [](auto a, auto b) { return a + b; };
std::cout << add(1, 2) << std::endl; // Output: 3
std::cout << add(1.5, 2.3) << std::endl; // Output: 3.8

2. 变量模板

变量模板允许为变量(而不仅是函数或类)定义模板。

示例:

template<typename T>
constexpr T pi = T(3.1415926535897932385L);

std::cout << pi<double> << std::endl; // Output: 3.14159
std::cout << pi<float> << std::endl; // Output: 3.14159f

3. 返回类型推导

C++14 允许函数使用auto自动推导返回类型。

示例:

auto add(int a, int b) {
    return a + b;
}

std::cout << add(1, 2) << std::endl; // Output: 3

4. std::make_unique

这个工具函数简化了std::unique_ptr实例的创建,使代码更可读、更不易出错。

示例:

#include <memory>

auto ptr = std::make_unique<int>(42);
std::cout << *ptr << std::endl; // Output: 42

5. deprecated 属性

[[deprecated]]属性可用于将函数和变量标记为已弃用,在使用它们时产生编译期警告。

示例:

[[deprecated("Use new_function() instead")]]
void old_function() {}

void new_function() {}

int main() {
    old_function(); // Warning: 'old_function' is deprecated: Use new_function() instead
    new_function();
}

6. 二进制字面量与数字分隔符

C++ 引入了以0b或0B为前缀的二进制字面量,并使用单引号作为数字分隔符,以提高大数字的可读性。

示例:

int binary = 0b1010; // Binary literal
int largeNumber = 1'000'000; // Digit separator

std::cout << binary << std::endl; // Output: 10
std::cout << largeNumber << std::endl; // Output: 1000000

C++17

C++17 带来了大量新特性和增强,使语言更具表达力且更易用。以下是一些主要新增特性的详细说明和示例:

1. std::optional

std::optional是一个工具类型,表示一个可能存在也可能不存在的值。它对于可能不返回值的函数很有用。

示例:

#include <optional>
#include <iostream>

std::optional<int> find_even_number(int num) {
    if (num % 2 == 0) return num;
    return std::nullopt;
}

int main() {
    auto result = find_even_number(4);
    if (result) {
        std::cout << "Even number: " << *result << "\n";
    } else {
        std::cout << "Not an even number\n";
    }
}

2. std::variant

std::variant是类型安全的联合体,允许变量保存若干指定类型之一。

示例:

#include <variant>
#include <iostream>

int main() {
    std::variant<int, float, std::string> my_variant;
    my_variant = 10;
    std::cout << std::get<int>(my_variant) << "\n";

    my_variant = 3.14f;
    std::cout << std::get<float>(my_variant) << "\n";

    my_variant = "Hello";
    std::cout << std::get<std::string>(my_variant) << "\n";
}

3. std::any

std::any是类型安全的容器,可保存任意类型的单个值。当值的类型在编译期未知时,它很有用。

示例:

#include <any>
#include <iostream>

int main() {
    std::any my_any;
    my_any = 42;
    std::cout << std::any_cast<int>(my_any) << "\n";

    my_any = std::string("Hello");
    std::cout << std::any_cast<std::string>(my_any) << "\n";
}

4. 结构化绑定

结构化绑定允许将类元组对象直接解包到独立的变量中。

示例:

#include <tuple>
#include <iostream>

std::tuple<int, float, std::string> get_data() {
    return {1, 2.3f, "test"};
}

int main() {
    auto [i, f, s] = get_data();
    std::cout << "i: " << i << ", f: " << f << ", s: " << s << "\n";
}

5. if constexpr

if constexpr支持编译期条件编译。

示例:

#include <iostream>

template <typename T>
void print_type_info(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "Integral type\n";
    } else {
        std::cout << "Non-integral type\n";
    }
}

int main() {
    print_type_info(42);
    print_type_info(3.14);
}

6. 折叠表达式

折叠表达式通过为参数包上的操作提供简洁语法,简化了可变参数模板的使用。

示例:

#include <iostream>

template<typename... Args>
auto sum(Args... args) {
    return (... + args); // fold expression
}

int main() {
    std::cout << "Sum: " << sum(1, 2, 3, 4, 5) << "\n";
}

7. 文件系统库

<filesystem>库提供了对文件系统及其组件执行操作的设施。

示例:

#include <filesystem>
#include <iostream>

int main() {
    std::filesystem::path p{"example.txt"};
    if (std::filesystem::exists(p)) {
        std::cout << p << " exists\n";
    } else {
        std::cout << p << " does not exist\n";
    }
}

这些特性共同增强了 C++ 的能力与灵活性,使编写健壮、现代、高效的代码更加容易。

C++20

C++20 是 C++ 演进中的一个里程碑,带来了一系列增强语言表达力、安全性和性能的强大特性。以下是对主要新增特性及示例的详细介绍:

1. 概念(Concepts)

概念提供了一种对模板参数指定约束的方式,使模板更可读、错误信息更易理解。

#include <concepts>
#include <iostream>

template <typename T>
concept Integral = std::is_integral_v<T>;

template <Integral T>
T add(T a, T b) {
    return a + b;
}

int main() {
    std::cout << add(3, 4) << '\n';  // Works
    // std::cout << add(3.0, 4.0) << '\n';  // Error: double doesn't satisfy Integral
}

2. 范围(Ranges)

Ranges 库引入了处理数据序列的新方式,使代码更可读、更具表达力。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    auto result = v | std::ranges::views::filter([](int n) { return n % 2 == 0; });

    for (int n : result) {
        std::cout << n << ' ';  // Output: 2 4
    }
}

3. 协程

协程使以顺序风格编写异步代码成为可能,简化了并发应用的开发。

#include <coroutine>
#include <iostream>

struct ReturnObject {
    struct promise_type {
        ReturnObject get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void unhandled_exception() {}
        void return_void() {}
    };
};

ReturnObject foo() {
    std::cout << "Hello ";
    co_await std::suspend_always{};
    std::cout << "World\n";
}

int main() {
    auto handle = foo();
    handle.resume();
    handle.resume();
}

4. 模块

模块提供了组织和导入代码的新方式,可缩短编译时间并增强代码封装性。

// my_module.ixx
export module my_module;
export int add(int a, int b) {
    return a + b;
}

// main.cpp
import my_module;
#include <iostream>

int main() {
    std::cout << add(3, 4) << '\n';  // Output: 7
}

5. 三路比较(太空船运算符)

三路比较运算符(<=>)简化了比较的实现,并提供了一致的处理方式。

#include <compare>
#include <iostream>

struct Point {
    int x, y;
    auto operator<=>(const Point&) const = default;
};

int main() {
    Point p1{1, 2}, p2{2, 3};
    if (p1 < p2) {
        std::cout << "p1 is less than p2\n";  // Output
    }
}

6. 日历与时区库

该库为处理日期、时间和时区提供了全面的支持。

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    auto now = system_clock::now();
    auto today = floor<days>(now);
    std::cout << "Today is: " << today.time_since_epoch().count() << " days since epoch\n";
}

C++20 极大地丰富了这门语言,使其在广泛的编程任务中更加现代和强大。这些特性共同提升了 C++ 代码的健壮性、可读性和可维护性。

C++23

C++23 引入了若干强大特性,增强了语言的能力、安全性和易用性。以下是一些最重要的新增特性的详细介绍:

1. std::expected

std::expected是用于错误处理的新工具,类似于std::optional,但内置了错误状态管理。

示例:

#include <iostream>
#include <expected>

std::expected<int, std::string> divide(int a, int b) {
    if (b == 0) {
        return std::unexpected("Division by zero!");
    }
    return a / b;
}

int main() {
    auto result = divide(10, 0);
    if (!result) {
        std::cout << "Error: " << result.error() << '\n';
    } else {
        std::cout << "Result: " << *result << '\n';
    }
}

2. std::mdspan

std::mdspan是多维数组视图,提供了一种描述数据在内存中形状和布局的方式。

示例:

#include <iostream>
#include <mdspan>

int main() {
    int data[6] = {1, 2, 3, 4, 5, 6};
    std::mdspan<int, std::extents<2, 3>> mdspan(data);
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) {
            std::cout << mdspan(i, j) << ' ';
        }
        std::cout << '\n';
    }
}

3. 反射(实验性)

C++23 引入了实验性的反射能力,允许对类型及其属性进行编译期内省。

示例:

#include <iostream>
#include <experimental/reflect>

struct MyStruct {
    int a;
    double b;
    void foo() {}
};

int main() {
    auto type = reflexpr(MyStruct);
    for (auto member : type.get_data_members()) {
        std::cout << "Member name: " << member.get_name() << '\n';
    }
}

4. if consteval

if consteval语句允许代码检查自身是否正在编译期求值。

示例:

#include <iostream>

constexpr int factorial(int n) {
    if consteval {
        if (n < 0) throw "Negative input!";
    }
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int main() {
    std::cout << factorial(5) << '\n'; // OK
    // std::cout << factorial(-1) << '\n'; // Compile-time error
}

5.[[assume]] 属性

[[assume]]属性允许开发者向优化器提供假设,通过告知编译器不变量来提升性能。

示例:

#include <iostream>

int main() {
    int x = 5;
    [[assume(x > 0)]];
    std::cout << "x is positive.\n";
}

这些特性显著增强了 C++ 的表达力、安全性和性能,使其依然是现代软件开发的稳健选择。不过,其中您已经用过多少了呢?

分享本文