名称: cpp-modern-features 用户可调用: false 描述: 当使用现代C++特性(C++11/14/17/20),包括auto、lambdas、range-based loops、structured bindings和concepts时使用。 允许工具:
- 读取
- 写入
- 编辑
- 搜索
- 全局
- Bash
现代C++特性
现代C++(C++11及以后)引入了重大改进,使C++更具表达性、更安全、更易用。此技能涵盖关键的现代特性,包括类型推断、lambda表达式、范围for循环、智能初始化以及最新的C++20新增内容。
自动类型推断
auto关键字启用自动类型推导,减少冗余,同时保持类型安全。
#include <iostream>
#include <vector>
#include <map>
#include <string>
void auto_examples() {
// 简单类型推断
auto x = 42; // int
auto pi = 3.14159; // double
auto name = "Alice"; // const char*
auto message = std::string("Hello"); // std::string
// 迭代器简化
std::vector<int> numbers = {1, 2, 3, 4, 5};
// C++11之前
for (std::vector<int>::iterator it = numbers.begin();
it != numbers.end(); ++it) {
std::cout << *it << " ";
}
// 使用auto
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it << " ";
}
// 复杂类型
std::map<std::string, std::vector<int>> data;
auto it = data.find("key"); // 比完整类型更清晰
// 返回类型推导 (C++14)
auto multiply = [](int a, int b) { return a * b; };
// 结构化绑定 (C++17)
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << "
";
}
}
Lambda表达式
Lambdas提供内联匿名函数,对现代C++算法和回调至关重要。
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
void lambda_examples() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
// 基本lambda
auto print = [](int n) { std::cout << n << " "; };
std::for_each(numbers.begin(), numbers.end(), print);
// 带捕获的lambda
int threshold = 5;
auto above_threshold = [threshold](int n) { return n > threshold; };
// 按值捕获 [=]
auto sum_above = [=]() {
int sum = 0;
for (int n : numbers) {
if (n > threshold) sum += n;
}
return sum;
};
// 按引用捕获 [&]
int count = 0;
auto count_above = [&count, threshold](int n) {
if (n > threshold) count++;
};
std::for_each(numbers.begin(), numbers.end(), count_above);
// 通用lambda (C++14)
auto generic_print = [](const auto& item) {
std::cout << item << " ";
};
// Lambda作为比较器
std::sort(numbers.begin(), numbers.end(),
[](int a, int b) { return a > b; }); // 降序
// 可变lambda
auto counter = [count = 0]() mutable {
return ++count;
};
std::cout << counter() << "
"; // 1
std::cout << counter() << "
"; // 2
}
// 返回lambda
std::function<int(int)> make_multiplier(int factor) {
return [factor](int n) { return n * factor; };
}
范围for循环
范围for循环提供对容器和范围的简洁、安全迭代。
#include <vector>
#include <map>
#include <string>
#include <iostream>
void range_based_loops() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 基本迭代
for (int n : numbers) {
std::cout << n << " ";
}
// 按引用(用于修改)
for (int& n : numbers) {
n *= 2;
}
// 按常量引用(对大对象高效)
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
for (const auto& name : names) {
std::cout << name << "
";
}
// 带结构化绑定 (C++17)
std::map<std::string, int> ages = {
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35}
};
for (const auto& [name, age] : ages) {
std::cout << name << " is " << age << " years old
";
}
// for循环中的初始化器 (C++20)
for (std::vector<int> temp = {1, 2, 3}; auto n : temp) {
std::cout << n << " ";
}
}
// 自定义范围支持
class Range {
int start_, end_;
public:
Range(int start, int end) : start_(start), end_(end) {}
struct Iterator {
int current;
Iterator(int val) : current(val) {}
int operator*() const { return current; }
Iterator& operator++() { ++current; return *this; }
bool operator!=(const Iterator& other) const {
return current != other.current;
}
};
Iterator begin() const { return Iterator(start_); }
Iterator end() const { return Iterator(end_); }
};
void use_custom_range() {
for (int i : Range(0, 10)) {
std::cout << i << " ";
}
}
统一初始化
使用大括号的统一初始化提供一致的语法并防止窄化转换。
#include <vector>
#include <string>
#include <map>
struct Point {
int x, y;
};
void uniform_initialization() {
// 内置类型
int a{42};
double pi{3.14159};
// 容器
std::vector<int> numbers{1, 2, 3, 4, 5};
std::map<std::string, int> ages{
{"Alice", 30},
{"Bob", 25}
};
// 聚合体
Point p{10, 20};
// 防止窄化
// int x{3.14}; // 编译错误!
int x = 3.14; // 编译通过(隐式转换)
// 空初始化(零/默认)
int zero{}; // 0
std::string empty{}; // ""
// 返回值
auto get_numbers = []() { return std::vector<int>{1, 2, 3}; };
}
// 最令人烦恼的解析解决
class Widget {
public:
Widget() = default;
Widget(int x) {}
};
void vexing_parse() {
// C++11之前:声明一个函数!
// Widget w();
// 现代C++:创建一个对象
Widget w{}; // 正确
Widget w2{10}; // 也正确
}
移动语义和右值引用
移动语义实现资源的高效转移而无需复制,对性能至关重要。
#include <vector>
#include <string>
#include <utility>
#include <iostream>
class Buffer {
size_t size_;
int* data_;
public:
// 构造函数
Buffer(size_t size) : size_(size), data_(new int[size]) {
std::cout << "Constructor
";
}
// 复制构造函数
Buffer(const Buffer& other)
: size_(other.size_), data_(new int[other.size_]) {
std::copy(other.data_, other.data_ + size_, data_);
std::cout << "Copy constructor
";
}
// 移动构造函数
Buffer(Buffer&& other) noexcept
: size_(other.size_), data_(other.data_) {
other.size_ = 0;
other.data_ = nullptr;
std::cout << "Move constructor
";
}
// 复制赋值
Buffer& operator=(const Buffer& other) {
if (this != &other) {
delete[] data_;
size_ = other.size_;
data_ = new int[size_];
std::copy(other.data_, other.data_ + size_, data_);
std::cout << "Copy assignment
";
}
return *this;
}
// 移动赋值
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data_;
size_ = other.size_;
data_ = other.data_;
other.size_ = 0;
other.data_ = nullptr;
std::cout << "Move assignment
";
}
return *this;
}
~Buffer() { delete[] data_; }
};
void move_semantics_example() {
Buffer b1(100);
Buffer b2 = std::move(b1); // 移动,而非复制
std::vector<Buffer> buffers;
buffers.push_back(Buffer(50)); // 使用移动构造函数
// 完美转发
auto make_buffer = [](auto&&... args) {
return Buffer(std::forward<decltype(args)>(args)...);
};
}
可变模板
可变模板使函数和类能接受任意数量的参数。
#include <iostream>
#include <string>
// 基本情况
void print() {
std::cout << "
";
}
// 递归可变模板
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...);
}
// 折叠表达式 (C++17)
template<typename... Args>
auto sum(Args... args) {
return (args + ...);
}
template<typename... Args>
auto sum_with_init(Args... args) {
return (args + ... + 0);
}
// 完美转发与可变模板
template<typename T, typename... Args>
std::unique_ptr<T> make_unique_custom(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
void variadic_examples() {
print(1, 2.5, "hello", std::string("world"));
auto total = sum(1, 2, 3, 4, 5); // 15
// 折叠表达式用于各种操作
auto all_true = [](auto... args) {
return (args && ...);
};
auto any_true = [](auto... args) {
return (args || ...);
};
}
结构化绑定 (C++17)
结构化绑定将对象分解为其组成部分,提高代码可读性。
#include <tuple>
#include <map>
#include <string>
#include <array>
struct Person {
std::string name;
int age;
double salary;
};
std::tuple<int, std::string, double> get_employee() {
return {42, "Alice", 75000.0};
}
void structured_bindings() {
// 元组分解
auto [id, name, salary] = get_employee();
// 对分解
std::pair<int, std::string> p{1, "one"};
auto [num, text] = p;
// 结构体分解
Person person{"Bob", 30, 80000.0};
auto [pname, page, psalary] = person;
// 数组分解
std::array<int, 3> arr{1, 2, 3};
auto [a, b, c] = arr;
// 映射迭代
std::map<std::string, int> scores{{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << "
";
}
// 引用
auto& [rname, rage, rsalary] = person;
rage = 31; // 修改person.age
}
概念 (C++20)
概念约束模板参数,提供更好的错误消息和更清晰的接口。
#include <concepts>
#include <iostream>
#include <vector>
// 定义自定义概念
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
// 使用概念约束模板
template<Numeric T>
T add(T a, T b) {
return a + b;
}
// 带多个约束的概念
template<typename T>
concept Printable = requires(T t) {
{ std::cout << t } -> std::convertible_to<std::ostream&>;
};
template<Printable T>
void print(const T& value) {
std::cout << value << "
";
}
// 范围概念
template<typename T>
concept Range = requires(T r) {
r.begin();
r.end();
};
template<Range R>
void print_range(const R& range) {
for (const auto& item : range) {
std::cout << item << " ";
}
std::cout << "
";
}
// 带关联类型的概念
template<typename T>
concept Container = requires(T c) {
typename T::value_type;
typename T::iterator;
{ c.begin() } -> std::same_as<typename T::iterator>;
{ c.end() } -> std::same_as<typename T::iterator>;
{ c.size() } -> std::convertible_to<std::size_t>;
};
template<Container C>
void process_container(const C& container) {
std::cout << "Size: " << container.size() << "
";
}
void concepts_example() {
auto result = add(5, 10); // 正确
auto dresult = add(5.5, 2.3); // 正确
// auto sresult = add("hi", "there"); // 错误:不满足Numeric概念
print(42);
print("Hello");
std::vector<int> vec{1, 2, 3};
print_range(vec);
process_container(vec);
}
范围库 (C++20)
范围库提供可组合的算法和视图,用于处理序列。
#include <ranges>
#include <vector>
#include <iostream>
#include <algorithm>
void ranges_examples() {
std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 视图是惰性的且可组合的
auto even = [](int n) { return n % 2 == 0; };
auto square = [](int n) { return n * n; };
// 无需中间容器组合操作
auto result = numbers
| std::views::filter(even)
| std::views::transform(square)
| std::views::take(3);
for (int n : result) {
std::cout << n << " "; // 4 16 36
}
std::cout << "
";
// 范围算法
std::ranges::sort(numbers, std::greater{});
// 带投影的查找
struct Person {
std::string name;
int age;
};
std::vector<Person> people{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35}
};
auto it = std::ranges::find(people, "Bob", &Person::name);
// 生成数字的views::iota
for (int i : std::views::iota(1, 6)) {
std::cout << i << " "; // 1 2 3 4 5
}
std::cout << "
";
// 拆分视图
std::string text = "one,two,three";
for (auto word : text | std::views::split(',')) {
for (char c : word) {
std::cout << c;
}
std::cout << " ";
}
}
最佳实践
- 对复杂类型和迭代器使用
auto,但简单类型保持明确 - 优先使用lambdas而非函数对象进行内联操作和回调
- 使用范围for循环代替手动迭代器操作
- 用
{}初始化变量以防止窄化转换 - 为资源拥有类实现移动构造函数和赋值
- 在所有权转移时使用
std::move,而非一般优化 - 优先使用结构化绑定而非
std::get<>()处理元组和对 - 使用概念约束模板并改进错误消息
- 利用范围进行序列的可组合惰性操作
- 对大对象使用
const auto&进行范围for循环
常见陷阱
- 过度使用
auto,在类型提供清晰性时降低代码可读性 - 在lambda中捕获引用但其捕获对象寿命超出范围
- 对const对象使用
std::move,禁用移动语义 - 忘记在移动操作上使用
noexcept,阻止优化 - 在范围for循环中修改容器
- 结构化绑定临时对象导致的悬垂引用
- 使用折叠表达式而不考虑运算符优先级
- 假设范围视图创建副本而非提供惰性视图
- 从后续将再次使用的对象移动
- 未将移动构造函数和赋值标记为
noexcept
何时使用现代C++特性
当需要时使用现代C++特性:
- 更简洁、更具表达性的代码,减少样板
- 更好的类型安全,通过概念和结构化绑定
- 通过移动语义提高性能
- 使用lambdas和范围的功能式编程模式
- 更少模板复杂度的泛型编程
- 通过智能指针更安全的资源管理
- 更易维护和重构的代码
- 通过概念获得更好的编译器错误消息
- 通过范围进行惰性求值和组合
- 从较旧的C++代码库迁移到现代标准