C++ 语言标准
C++ 标准概述
C++ 语言由 ISO/IEC 标准化,每个版本都引入了新的特性和改进。
| 版本 | 发布年份 | 正式名称 | 主要特性 |
|---|---|---|---|
| C++98 | 1998 | ISO/IEC 14882:1998 | 第一个标准 |
| C++03 | 2003 | ISO/IEC 14882:2003 | C++98 的技术勘误 |
| C++11 | 2011 | ISO/IEC 14882:2011 | 重大更新(现代 C++) |
| C++14 | 2014 | ISO/IEC 14882:2014 | C++11 的小更新 |
| C++17 | 2017 | ISO/IEC 14882:2017 | 主要增强 |
| C++20 | 2020 | ISO/IEC 14882:2020 | 新特性(模块、协程等) |
| C++23 | 2023 | ISO/IEC 14882:2023 | 最新标准 |
C++11(现代 C++)
类型推断
cpp
// auto 关键字
auto x = 42; // int
auto y = 3.14; // double
auto z = "hello"; // const char*
auto vec = std::vector<int>(); // std::vector<int>
// decltype
int a = 10;
decltype(a) b = 20; // int
// 返回类型推断
auto add(int x, int y) -> int {
return x + y;
}智能指针
cpp
#include <memory>
// unique_ptr - 独占所有权
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
// std::unique_ptr<int> ptr2 = ptr1; // 错误:不能复制
std::unique_ptr<int> ptr2 = std::move(ptr1); // 可以移动
// shared_ptr - 共享所有权
std::shared_ptr<int> sp1 = std::make_shared<int>(42);
std::shared_ptr<int> sp2 = sp1; // 多个指针共享所有权
std::cout << sp1.use_count() << std::endl; // 2
// weak_ptr - 弱引用,不增加引用计数
std::weak_ptr<int> wp = sp1;
if (auto locked = wp.lock()) {
std::cout << *locked << std::endl;
}Lambda 表达式
cpp
// 基本语法
auto lambda = [](int x) { return x * x; };
int result = lambda(5); // 25
// 带捕获
int multiplier = 2;
auto multiply = [multiplier](int x) { return x * multiplier; };
// 可变捕获
int counter = 0;
auto increment = [counter]() mutable {
counter++;
return counter;
};
// 在算法中使用
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::for_each(numbers.begin(), numbers.end(), [](int& x) {
x *= 2;
});
// 带谓词
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b; // 降序排序
});右值引用和移动语义
cpp
// 右值引用
int&& rref = 42;
// 移动构造函数
class Resource {
private:
int* data;
size_t size;
public:
Resource(size_t s) : size(s), data(new int[s]) {}
// 移动构造函数
Resource(Resource&& other) noexcept : size(other.size), data(other.data) {
other.data = nullptr;
other.size = 0;
}
// 移动赋值运算符
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
~Resource() {
delete[] data;
}
};
// std::move
Resource createResource() {
return Resource(100);
}
Resource r1 = createResource(); // 移动构造
Resource r2 = std::move(r1); // 显式移动范围 for 循环
cpp
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 只读访问
for (int num : numbers) {
std::cout << num << " ";
}
// 修改元素
for (int& num : numbers) {
num *= 2;
}
// 使用 auto
for (const auto& num : numbers) {
std::cout << num << " ";
}
// 初始化列表
for (auto x : {1, 2, 3, 4, 5}) {
std::cout << x << " ";
}新容器
cpp
#include <array>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
// array - 固定大小数组
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// unordered_map - 哈希表
std::unordered_map<std::string, int> hashmap;
hashmap["apple"] = 1;
hashmap["banana"] = 2;
// unordered_set - 哈希集合
std::unordered_set<int> hashset;
hashset.insert(1);
hashset.insert(2);
// tuple - 元组
std::tuple<int, double, std::string> myTuple(42, 3.14, "hello");
int first = std::get<0>(myTuple);
double second = std::get<1>(myTuple);
std::string third = std::get<2>(myTuple);
// 结构化绑定(C++17)
auto [i, d, s] = myTuple;线程支持
cpp
#include <thread>
#include <mutex>
#include <future>
// 创建线程
void worker(int id) {
std::cout << "Worker " << id << std::endl;
}
std::thread t1(worker, 1);
std::thread t2(worker, 2);
t1.join();
t2.join();
// 互斥锁
std::mutex mtx;
int counter = 0;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
counter++;
}
// 异步操作
std::future<int> future = std::async(std::launch::async, [](){
return 42;
});
int result = future.get();其他特性
cpp
// nullptr 代替 NULL
int* ptr = nullptr;
// constexpr - 编译时常量
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(5); // 120
// noexcept - 不抛出异常
void safeFunction() noexcept {
// 保证不会抛出异常
}
// override 和 final
class Base {
public:
virtual void func() {}
};
class Derived : public Base {
public:
void func() override {} // 明确覆盖
};
// = delete 和 = default
class MyClass {
public:
MyClass() = default; // 使用默认构造函数
MyClass(const MyClass&) = delete; // 禁止拷贝构造
};C++14
泛型 lambda
cpp
// C++11
auto lambda = [](int x, int y) { return x + y; };
// C++14 - 自动推断参数类型
auto genericLambda = [](auto x, auto y) {
return x + y;
};
int sum1 = genericLambda(1, 2); // int
double sum2 = genericLambda(1.5, 2.5); // double返回类型推断
cpp
// C++11
auto add(int x, int y) -> int {
return x + y;
}
// C++14 - 自动推断返回类型
auto add(int x, int y) {
return x + y;
}make_unique
cpp
// C++11
std::unique_ptr<int> ptr(new int(42));
// C++14 - 更安全的方式
auto ptr = std::make_unique<int>(42);二进制字面量
cpp
int binary = 0b1010; // 10
int octal = 0o755; // 八进制字面量变量模板
cpp
template<typename T>
constexpr T pi = T(3.1415926535897932385L);
int intPi = pi<int>;
double doublePi = pi<double>;C++17
结构化绑定
cpp
std::pair<int, std::string> pair = {42, "hello"};
auto [num, str] = pair;
std::tuple<int, double, std::string> tuple = {42, 3.14, "world"};
auto [i, d, s] = tuple;
// 在范围 for 循环中使用
std::map<std::string, int> map = {{"a", 1}, {"b", 2}};
for (const auto& [key, value] : map) {
std::cout << key << ": " << value << std::endl;
}if constexpr
cpp
template<typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>) {
return *t; // 如果是指针
} else {
return t; // 如果不是指针
}
}
int x = 42;
int* ptr = &x;
std::cout << get_value(x) << std::endl; // 42
std::cout << get_value(ptr) << std::endl; // 42std::optional
cpp
#include <optional>
std::optional<int> divide(int a, int b) {
if (b == 0) {
return std::nullopt; // 无效值
}
return a / b;
}
auto result = divide(10, 2);
if (result) {
std::cout << *result << std::endl; // 5
}
result = divide(10, 0);
if (!result) {
std::cout << "Division by zero!" << std::endl;
}std::variant
cpp
#include <variant>
std::variant<int, double, std::string> value;
value = 42;
std::cout << std::get<int>(value) << std::endl;
value = 3.14;
if (std::holds_alternative<double>(value)) {
std::cout << std::get<double>(value) << std::endl;
}
value = "hello";
std::visit([](auto&& arg) {
std::cout << arg << std::endl;
}, value);std::any
cpp
#include <any>
std::any value = 42;
value = 3.14;
value = "hello";
if (value.type() == typeid(std::string)) {
std::string str = std::any_cast<std::string>(value);
std::cout << str << std::endl;
}并行算法
cpp
#include <algorithm>
#include <execution>
std::vector<int> numbers(1000000);
std::iota(numbers.begin(), numbers.end(), 1);
// 并行排序
std::sort(std::execution::par, numbers.begin(), numbers.end());
// 并行 for_each
std::for_each(std::execution::par, numbers.begin(), numbers.end(),
[](int& x) { x *= 2; });文件系统
cpp
#include <filesystem>
namespace fs = std::filesystem;
// 检查文件是否存在
if (fs::exists("test.txt")) {
std::cout << "File exists" << std::endl;
}
// 创建目录
fs::create_directory("new_folder");
// 复制文件
fs::copy_file("source.txt", "destination.txt");
// 遍历目录
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << std::endl;
}
// 获取文件大小
if (fs::is_regular_file("test.txt")) {
auto size = fs::file_size("test.txt");
std::cout << "File size: " << size << " bytes" << std::endl;
}std::string_view
cpp
#include <string_view>
void printString(std::string_view sv) {
std::cout << sv << std::endl;
}
std::string str = "hello";
printString(str); // 从 std::string
printString("world"); // 从字符串字面量
printString(str.substr(0, 3)); // 从子串C++20
模块
cpp
// math.ixx - 模块接口
export module math;
export int add(int a, int b) {
return a + b;
}
export double multiply(double a, double b) {
return a * b;
}
// main.cpp
import math;
#include <iostream>
int main() {
std::cout << add(1, 2) << std::endl;
std::cout << multiply(2.0, 3.0) << std::endl;
return 0;
}协程
cpp
#include <coroutine>
#include <iostream>
struct Task {
struct promise_type {
Task get_return_object() {
return {};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
Task coroutine() {
std::cout << "Coroutine start" << std::endl;
co_await std::suspend_always{};
std::cout << "Coroutine resume" << std::endl;
}
int main() {
auto task = coroutine();
return 0;
}Concepts(概念)
cpp
#include <concepts>
template<typename T>
concept Integral = std::is_integral_v<T>;
template<Integral T>
T add(T a, T b) {
return a + b;
}
// 或使用 requires
template<typename T>
requires std::is_integral_v<T>
T multiply(T a, T b) {
return a * b;
}
// 使用 requires 子句
auto print = [](const auto& container) requires requires {
{ container.size() } -> std::convertible_to<size_t>;
} {
std::cout << container.size() << std::endl;
};范围
cpp
#include <ranges>
#include <vector>
#include <algorithm>
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 过滤偶数
auto evens = numbers | std::views::filter([](int x) {
return x % 2 == 0;
});
// 转换为平方
auto squares = numbers | std::views::transform([](int x) {
return x * x;
});
// 组合视图
auto result = numbers
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
for (int x : result) {
std::cout << x << " ";
}三向比较(Spaceship 运算符)
cpp
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default;
};
Point p1{1, 2};
Point p2{1, 3};
if (p1 < p2) {
std::cout << "p1 < p2" << std::endl;
}std::span
cpp
#include <span>
void printArray(std::span<int> arr) {
for (int x : arr) {
std::cout << x << " ";
}
std::cout << std::endl;
}
int main() {
int arr1[] = {1, 2, 3, 4, 5};
std::vector<int> vec = {1, 2, 3, 4, 5};
printArray(arr1); // 从 C 数组
printArray(vec); // 从 vector
printArray({1, 2, 3}); // 从初始化列表
return 0;
}其他特性
cpp
// consteval - 强制编译时求值
consteval int square(int x) {
return x * x;
}
constexpr int result = square(5); // OK
// int runtimeValue = square(5); // 错误
// constinit - 常量初始化
constinit int globalVar = 42;
// 设计初始化符
struct Point {
int x, y;
};
Point p1{.x = 1, .y = 2}; // 指定成员初始化
// 范围 for 循环初始化
std::vector<int> vec;
for (auto x : vec = {1, 2, 3, 4, 5}) {
std::cout << x << " ";
}C++23
标准库模块
cpp
import std;
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}Deducing this(推断 this)
cpp
struct Widget {
void print() const & {
std::cout << "Lvalue" << std::endl;
}
void print() && {
std::cout << "Rvalue" << std::endl;
}
};
Widget w;
w.print(); // Lvalue
Widget{}.print(); // Rvaluestd::print 和 std::format
cpp
#include <print>
std::print("Hello, World!\n");
std::print("The answer is {}\n", 42);
std::print("Values: {}, {}, {}\n", 1, 2, 3);其他改进
cpp
// 改进的 if constexpr
template<typename T>
void process(T value) {
if constexpr (requires { value.size(); }) {
std::cout << "Has size: " << value.size() << std::endl;
} else {
std::cout << "No size method" << std::endl;
}
}版本选择
如何指定 C++ 标准
bash
# GCC/Clang
g++ -std=c++11 file.cpp
g++ -std=c++17 file.cpp
g++ -std=c++20 file.cpp
# MSVC
cl /std:c++17 file.cpp
cl /std:c++20 file.cpp
# CMake
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)特性检查
cpp
#if __cplusplus >= 202002L
// C++20 或更高
std::cout << "C++20 or later" << std::endl;
#elif __cplusplus >= 201703L
// C++17
std::cout << "C++17" << std::endl;
#elif __cplusplus >= 201402L
// C++14
std::cout << "C++14" << std::endl;
#elif __cplusplus >= 201103L
// C++11
std::cout << "C++11" << std::endl;
#endif最佳实践
1. 优先使用现代 C++ 特性
cpp
// 旧方式
int* arr = new int[10];
// ... 使用
delete[] arr;
// 新方式
std::vector<int> arr(10);
// 自动管理2. 使用智能指针
cpp
// 旧方式
MyClass* obj = new MyClass();
// ... 使用
delete obj;
// 新方式
auto obj = std::make_unique<MyClass>();
// 自动清理3. 使用范围 for 循环
cpp
// 旧方式
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << std::endl;
}
// 新方式
for (const auto& item : vec) {
std::cout << item << std::endl;
}4. 使用 auto 减少类型冗余
cpp
// 旧方式
std::map<std::string, std::vector<int>>::iterator it = map.begin();
// 新方式
auto it = map.begin();