·49 分钟
现代 C++ 新特性:从 C++11 到 C++20
C++C++11C++14C++17C++20C/C++
C++11 新特性
1. 自动类型推导(auto)
cpp
// auto 关键字
auto i = 42; // int
auto d = 3.14; // double
auto s = "hello"; // const char*
auto str = std::string("hello"); // std::string
// 自动推导复杂类型
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // std::vector<int>::iterator
// 函数返回类型推导(C++14)
auto add(int a, int b) {
return a + b; // 返回类型推导为 int
}
// decltype - 获取表达式类型
int x = 10;
decltype(x) y = 20; // y 的类型是 int
// decltype(auto) - 保留引用(C++14)
int& getRef();
decltype(auto) ref = getRef(); // 保留引用
2. 范围 for 循环
cpp
#include <vector>
#include <map>
// 基本用法
std::vector<int> vec = {1, 2, 3, 4, 5};
for (int x : vec) {
std::cout << x << " ";
}
// 使用引用避免拷贝
for (const auto& x : vec) {
std::cout << x << " ";
}
// 修改元素
for (auto& x : vec) {
x *= 2;
}
// 键值对遍历
std::map<std::string, int> map = {{"a", 1}, {"b", 2}};
for (const auto& [key, value] : map) { // C++17 结构化绑定
std::cout << key << ": " << value << std::endl;
}
3. Lambda 表达式
cpp
// 基本语法
auto greet = []() {
std::cout << "Hello!" << std::endl;
};
greet();
// 带参数
auto add = [](int a, int b) {
return a + b;
};
int sum = add(1, 2);
// 捕获变量
int x = 10;
int y = 20;
auto capture = [x, y]() { // 值捕获
return x + y;
};
auto captureRef = [&x, &y]() { // 引用捕获
x += 10;
return x + y;
};
auto captureAll = [=]() { // 捕获所有变量(值)
return x + y;
};
auto captureAllRef = [&]() { // 捕获所有变量(引用)
x += 10;
return x + y;
};
// 混合捕获
auto mixed = [x, &y]() {
y += x;
return y;
};
// 可变 Lambda
auto mutableLambda = [x]() mutable {
x += 10; // 可以修改捕获的值
return x;
};
// 泛型 Lambda(C++14)
auto generic = [](auto a, auto b) {
return a + b;
};
int result1 = generic(1, 2);
double result2 = generic(1.5, 2.5);
4. 移动语义
cpp
#include <string>
#include <vector>
#include <utility>
// 左值和右值
int x = 10; // x 是左值
int&& rref = 10; // 10 是右值,rref 是右值引用
// 移动构造函数
class MyString {
private:
char* data;
size_t size;
public:
// 普通构造函数
MyString(const char* str) {
size = strlen(str);
data = new char[size + 1];
strcpy(data, str);
}
// 拷贝构造函数
MyString(const MyString& other)
: size(other.size), data(new char[other.size + 1]) {
strcpy(data, other.data);
}
// 移动构造函数
MyString(MyString&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// 移动赋值运算符
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
~MyString() {
delete[] data;
}
};
// std::move - 转换为右值
MyString s1("hello");
MyString s2 = std::move(s1); // 移动,不是拷贝
// 完美转发
template<typename T>
void wrapper(T&& arg) {
target(std::forward<T>(arg)); // 保持值类别
}
5. 智能指针
cpp
#include <memory>
// unique_ptr - 独占所有权
std::unique_ptr<int> p1(new int(10));
auto p2 = std::make_unique<int>(20); // C++14
// 不能拷贝,只能移动
// std::unique_ptr<int> p3 = p1; // 错误
std::unique_ptr<int> p3 = std::move(p1);
// shared_ptr - 共享所有权
std::shared_ptr<int> sp1(new int(10));
auto sp2 = std::make_shared<int>(20);
std::shared_ptr<int> sp3 = sp1; // 引用计数 +1
// weak_ptr - 弱引用
std::weak_ptr<int> wp = sp1;
if (auto locked = wp.lock()) {
// 使用 locked
}
// 自定义删除器
auto deleter = [](int* p) {
std::cout << "Deleting " << *p << std::endl;
delete p;
};
std::unique_ptr<int, decltype(deleter)> p4(new int(10), deleter);
6. 右值引用和完美转发
cpp
#include <utility>
// 右值引用
void process(int& x) {
std::cout << "Lvalue: " << x << std::endl;
}
void process(int&& x) {
std::cout << "Rvalue: " << x << std::endl;
}
int main() {
int a = 10;
process(a); // 调用 Lvalue 版本
process(10); // 调用 Rvalue 版本
process(std::move(a)); // 调用 Rvalue 版本
}
// 完美转发
template<typename T>
void wrapper(T&& arg) {
process(std::forward<T>(arg));
}
// 万能引用
template<typename T>
void universal(T&& arg) {
// T 可以是左值引用或右值引用
}
// 折叠规则
// T& & → T&
// T& && → T&
// T&& & → T&
// T&& && → T&&
7. 初始化列表
cpp
#include <initializer_list>
#include <vector>
// 统一初始化
int arr[]{1, 2, 3, 4, 5};
std::vector<int> vec{1, 2, 3, 4, 5};
// 类初始化
class Point {
int x, y;
public:
Point(int x, int y) : x(x), y(y) {}
};
Point p{1, 2};
// 初始化列表
class MyContainer {
std::vector<int> data;
public:
MyContainer(std::initializer_list<int> list)
: data(list) {}
};
MyContainer container{1, 2, 3, 4, 5};
// 聚合初始化
struct Data {
int a;
double b;
std::string c;
};
Data d{1, 3.14, "hello"};
C++14 新特性
1. 泛型 Lambda
cpp
// 泛型 Lambda
auto add = [](auto a, auto b) {
return a + b;
};
int result1 = add(1, 2);
double result2 = add(1.5, 2.5);
std::string result3 = add(std::string("hello"), std::string(" world"));
// 模板 Lambda(C++20)
auto genericLambda = []<typename T>(T a, T b) {
return a + b;
};
2. 返回类型推导
cpp
// 自动推导返回类型
auto factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// 复杂返回类型
auto getData() {
return std::make_tuple(1, 3.14, "hello");
}
// 尾置返回类型(C++11)
auto add(int a, int b) -> int {
return a + b;
}
3. 变量模板
cpp
// 变量模板
template<typename T>
constexpr T pi = T(3.1415926535897932385);
double d = pi<double>;
float f = pi<float>;
// 常量表达式
template<int N>
constexpr int factorial = N * factorial<N - 1>;
template<>
constexpr int factorial<0> = 1;
int result = factorial<5>; // 编译时计算
4. std::make_unique
cpp
#include <memory>
// C++11
std::unique_ptr<int> p1(new int(10));
// C++14
auto p2 = std::make_unique<int>(10);
// 数组版本
auto p3 = std::make_unique<int[]>(10);
// 优势:
// 1. 更安全(避免裸 new)
// 2. 更高效(单次内存分配)
// 3. 异常安全
C++17 新特性
1. 结构化绑定
cpp
#include <tuple>
#include <map>
// 元组绑定
auto [x, y, z] = std::make_tuple(1, 3.14, "hello");
// 数组绑定
int arr[] = {1, 2, 3};
auto [a, b, c] = arr;
// 结构体绑定
struct Point {
int x;
int y;
};
Point p{1, 2};
auto [px, py] = p;
// 键值对绑定
std::map<std::string, int> map = {{"a", 1}, {"b", 2}};
for (const auto& [key, value] : map) {
std::cout << key << ": " << value << std::endl;
}
// 返回多个值
auto getMinMax(const std::vector<int>& vec) {
auto [min, max] = std::minmax_element(vec.begin(), vec.end());
return std::make_pair(*min, *max);
}
2. std::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: " << *result << std::endl;
}
// 默认值
int value = result.value_or(0);
// std::optional 作为成员
class Config {
std::optional<std::string> name;
std::optional<int> timeout;
public:
void setName(const std::string& n) { name = n; }
void setTimeout(int t) { timeout = t; }
std::string getName() const {
return name.value_or("default");
}
};
3. std::variant
cpp
#include <variant>
#include <string>
// 类型安全的联合
std::variant<int, double, std::string> value;
// 赋值
value = 42;
value = 3.14;
value = "hello";
// 访问
std::visit([](auto&& arg) {
std::cout << arg << std::endl;
}, value);
// 获取值
int i = std::get<int>(value); // 如果类型不匹配,抛异常
// 安全获取
if (auto* p = std::get_if<int>(&value)) {
std::cout << "Int: " << *p << std::endl;
}
// 访问者模式
struct Visitor {
void operator()(int i) const {
std::cout << "Int: " << i << std::endl;
}
void operator()(double d) const {
std::cout << "Double: " << d << std::endl;
}
void operator()(const std::string& s) const {
std::cout << "String: " << s << std::endl;
}
};
std::visit(Visitor{}, value);
4. std::any
cpp
#include <any>
// 任意类型
std::any value = 42;
value = 3.14;
value = std::string("hello");
// 获取值
int i = std::any_cast<int>(value); // 类型不匹配抛异常
// 安全获取
if (auto* p = std::any_cast<int>(&value)) {
std::cout << "Int: " << *p << std::endl;
}
// 检查类型
if (value.type() == typeid(int)) {
std::cout << "Is int" << std::endl;
}
// 应用场景
class AnyContainer {
std::map<std::string, std::any> data;
public:
template<typename T>
void set(const std::string& key, T value) {
data[key] = value;
}
template<typename T>
T get(const std::string& key) const {
return std::any_cast<T>(data.at(key));
}
};
5. if constexpr
cpp
// 编译时条件判断
template<typename T>
auto get_value(T t) {
if constexpr (std::is_integral_v<T>) {
return t * 2;
} else if constexpr (std::is_floating_point_v<T>) {
return t * 1.5;
} else {
return t;
}
}
// SFINAE 的替代
template<typename T>
void process(T value) {
if constexpr (std::is_arithmetic_v<T>) {
std::cout << "Arithmetic: " << value << std::endl;
} else {
std::cout << "Non-arithmetic" << std::endl;
}
}
// 递归展开
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first;
if constexpr (sizeof...(args) > 0) {
std::cout << ", ";
print(args...);
} else {
std::cout << std::endl;
}
}
6. 折叠表达式
cpp
// 一元折叠
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 右折叠
}
// 二元折叠
template<typename... Args>
auto sum_with_init(Args... args) {
return (args + ... + 0); // 带初始值
}
// 逻辑折叠
template<typename... Args>
bool all_true(Args... args) {
return (args && ...);
}
// 逗号折叠
template<typename... Args>
void print_all(Args... args) {
((std::cout << args << " "), ...);
std::cout << std::endl;
}
// 使用示例
int result = sum(1, 2, 3, 4, 5); // 15
bool all = all_true(true, true, false); // false
print_all(1, 2.5, "hello", 'c');
C++20 新特性
1. 概念(Concepts)
cpp
#include <concepts>
// 定义概念
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<typename T>
concept Hashable = requires(T t) {
{ std::hash<T>{}(t) } -> std::convertible_to<std::size_t>;
};
// 使用概念
template<Numeric T>
T add(T a, T b) {
return a + b;
}
// 约束 auto
Numeric auto value = 42;
// requires 表达式
template<typename T>
concept Printable = requires(T t) {
{ std::cout << t } -> std::same_as<std::ostream&>;
};
// 组合概念
template<typename T>
concept SignedNumeric = Numeric<T> && std::is_signed_v<T>;
// 标准库概念
template<std::default_initializable T>
T create() {
return T();
}
2. 范围(Ranges)
cpp
#include <ranges>
#include <vector>
// 视图
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 过滤偶数
auto even = vec | std::views::filter([](int x) {
return x % 2 == 0;
});
// 转换
auto doubled = vec | std::views::transform([](int x) {
return x * 2;
});
// 组合
auto result = vec
| std::views::filter([](int x) { return x > 3; })
| std::views::transform([](int x) { return x * 2; })
| std::views::take(3);
// 范围算法
std::ranges::sort(vec);
auto it = std::ranges::find(vec, 5);
// 生成器
auto iota = std::views::iota(1, 10); // 1, 2, ..., 9
auto repeat = std::views::repeat(42); // 42, 42, ...
3. 协程(Coroutines)
cpp
#include <coroutine>
#include <iostream>
// 协程任务类型
template<typename T>
struct Task {
struct promise_type {
T value;
Task get_return_object() {
return Task{
std::coroutine_handle<promise_type>::from_promise(*this)
};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(T v) {
value = v;
}
void unhandled_exception() {
std::terminate();
}
};
std::coroutine_handle<promise_type> handle;
T get() {
return handle.promise().value;
}
};
// 协程函数
Task<int> compute(int x) {
co_await std::suspend_always{};
co_return x * x;
}
// 异步生成器
template<typename T>
struct Generator {
struct promise_type {
T value;
Generator get_return_object() {
return Generator{
std::coroutine_handle<promise_type>::from_promise(*this)
};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T v) {
value = v;
return {};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle;
// 迭代器
struct iterator {
std::coroutine_handle<promise_type> handle;
iterator& operator++() {
handle.resume();
return *this;
}
T operator*() const {
return handle.promise().value;
}
bool operator==(std::default_sentinel_t) const {
return handle.done();
}
};
iterator begin() {
return {handle};
}
std::default_sentinel_t end() {
return {};
}
};
Generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto temp = a;
a = b;
b = temp + b;
}
}
4. 三向比较运算符(Spaceship Operator)
cpp
#include <compare>
// 自动生成比较运算符
struct Point {
int x, y;
auto operator==(const Point&) const = default;
auto operator<=>(const Point&) const = default;
};
// 使用
Point p1{1, 2}, p2{1, 3};
auto result = p1 <=> p2;
if (result < 0) {
std::cout << "p1 < p2" << std::endl;
} else if (result > 0) {
std::cout << "p1 > p2" << std::endl;
} else {
std::cout << "p1 == p2" << std::endl;
}
// 比较类别
// std::strong_ordering - 全序,可替换
// std::weak_ordering - 全序,不可替换
// std::partial_ordering - 偏序(如浮点数)
5. 模块(Modules)
cpp
// math.cppm (模块接口)
export module math;
export int add(int a, int b) {
return a + b;
}
export class Calculator {
public:
int multiply(int a, int b) {
return a * b;
}
};
// main.cpp
import math;
import <iostream>;
int main() {
std::cout << add(1, 2) << std::endl;
Calculator calc;
std::cout << calc.multiply(3, 4) << std::endl;
return 0;
}
// 优势:
// 1. 更快的编译速度
// 2. 更好的封装
// 3. 避免宏泄漏
// 4. 消除头文件问题
6. std::format
cpp
#include <format>
#include <iostream>
// 格式化字符串
std::string s1 = std::format("Hello, {}!", "world");
std::string s2 = std::format("The answer is {}", 42);
std::string s3 = std::format("{:.2f}", 3.14159);
// 命名参数
std::string s4 = std::format("{name} is {age} years old",
std::format_arg("name", "Alice"),
std::format_arg("age", 30));
// 对齐和填充
std::string s5 = std::format("{:<10}", "left"); // 左对齐
std::string s6 = std::format("{:>10}", "right"); // 右对齐
std::string s7 = std::format("{:^10}", "center"); // 居中
std::string s8 = std::format("{:*^10}", "center"); // 填充
// 数字格式
std::string s9 = std::format("{:b}", 42); // 二进制
std::string s10 = std::format("{:o}", 42); // 八进制
std::string s11 = std::format("{:x}", 42); // 十六进制
// 自定义类型
struct Point {
int x, y;
};
template<>
struct std::formatter<Point> {
constexpr auto parse(auto& ctx) {
return ctx.begin();
}
auto format(const Point& p, auto& ctx) const {
return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
Point p{1, 2};
std::string s12 = std::format("{}", p);
总结
版本演进
| 版本 | 关键特性 |
|---|---|
| C++11 | auto、Lambda、移动语义、智能指针 |
| C++14 | 泛型 Lambda、返回类型推导、make_unique |
| C++17 | 结构化绑定、optional、variant、any |
| C++20 | 概念、范围、协程、模块、format |
选择建议
- 新项目:直接使用 C++20
- 旧项目升级:逐步引入新特性
- 跨平台项目:考虑编译器支持
- 性能关键:重点关注移动语义和智能指针
学习路径
- C++11:基础现代 C++
- C++14:增强和优化
- C++17:实用工具
- C++20:高级特性
学习资源
延伸阅读: