C++17:重大更新

C++17 带来了许多重要的新特性,显著提升了 C++ 的表达力和安全性。

结构化绑定

// 解构 pair
std::pair<int, std::string> p{42, "hello"};
auto [id, name] = p;

// 解构 tuple
std::tuple<int, double, std::string> t{1, 3.14, "world"};
auto [x, y, z] = t;

// 解构数组
int arr[3] = {1, 2, 3};
auto [a, b, c] = arr;

// 解构结构体
struct Point {
    int x;
    int y;
};
Point pt{10, 20};
auto [px, py] = pt;

// 在范围 for 循环中使用
std::map<std::string, int> m{{"a", 1}, {"b", 2}};
for (const auto& [key, value] : m) {
    std::cout << key << ": " << value << "\n";
}

if 和 switch 初始化语句

// 传统方式
std::map<int, std::string> m;
auto it = m.find(42);
if (it != m.end()) {
    // 使用 it
}

// C++17 方式
if (auto it = m.find(42); it != m.end()) {
    // 使用 it
}

// 配合结构化绑定
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
if (auto [it, inserted] = m.emplace(3, "three"); inserted) {
    std::cout << "Inserted: " << it->second << "\n";
}

// switch 初始化语句
switch (auto it = m.find(42); auto result = it->second) {
    case "found":
        break;
}

constexpr if

template<typename T>
auto getValue(T t) {
    if constexpr (std::is_pointer_v<T>) {
        return *t;
    } else {
        return t;
    }
}

int x = 42;
std::cout << getValue(x) << "\n";      // 42
std::cout << getValue(&x) << "\n";     // 42

折叠表达式

// 参数包展开
template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // 右折叠
}

// 使用
int total = sum(1, 2, 3, 4, 5);  // 15

// 左折叠
template<typename... Args>
auto printAll(Args... args) {
    (std::cout << ... << args) << "\n";
}

printAll(1, " ", 2, " ", 3);  // 1 2 3

// 空参数包
template<typename... Args>
void log(Args... args) {
    ((std::cout << args << "\n"), ...);
}

内联变量

// 头文件中定义静态成员变量
struct MyClass {
    static inline int value = 42;  // C++17
    static inline std::vector<int> data = {1, 2, 3};
};

// 全局变量
inline constexpr int MAX_SIZE = 1000;

// 不再需要在 .cpp 文件中定义

类模板参数推导(CTAD)

std::pair p{42, "hello"};           // std::pair<int, const char*>
std::tuple t{1, 2.0, "three"};      // std::tuple<int, double, const char*>

// 推导指引
template<typename T>
struct MyContainer {
    MyContainer(T val) : value_(val) {}
    T value_;
};

MyContainer c{42};  // MyContainer<int>

// 容器推导
std::vector v{1, 2, 3};  // std::vector<int>

嵌套命名空间

// C++17 之前
namespace outer::inner {
    void func() {}
}

// 等同于
namespace outer {
    namespace inner {
        void func() {}
    }
}

属性改进

// [[nodiscard]]:忽略返回值会警告
[[nodiscard]] int compute() {
    return 42;
}

// compute();  // 警告:返回值被忽略

// [[maybe_unused]]:抑制未使用警告
[[maybe_unused]] int x = 42;

// [[fallthrough]]:显式表示 switch 穿透
switch (value) {
    case 1:
        doSomething();
        [[fallthrough]];
    case 2:
        doSomethingElse();
        break;
}

// [[nodiscard("reason")]] 自定义消息
[[nodiscard("Connection must be closed")]]
Connection connect();

UTF-8 字符字面量

// UTF-8 字符字面量
char8_t c1 = u8'A';  // UTF-8 编码

// UTF-8 字符串字面量
auto str = u8"Hello, 世界!";  // const char8_t*

// Unicode 编码
char16_t c2 = u'汉';  // UTF-16
char32_t c3 = U'中';  // UTF-32

std::optional

#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 << "\n";  // 5
}

// 使用 value_or 提供默认值
int value = divide(10, 0).value_or(-1);  // -1

// 直接构造
std::optional<std::string> opt("hello");

std::variant

#include <variant>

std::variant<int, double, std::string> var;

var = 42;
std::cout << std::get<int>(var) << "\n";  // 42

// 使用 std::visit
auto visitor = [](auto&& arg) {
    std::cout << arg << "\n";
};
std::visit(visitor, var);

// 使用 std::holds_alternative 检查类型
if (std::holds_alternative<int>(var)) {
    std::cout << "Contains int\n";
}

// 获取索引
std::cout << var.index() << "\n";  // 0

std::any

#include <any>

std::any a = 42;
a = 3.14;
a = std::string("hello");

// 检查类型
if (a.type() == typeid(std::string)) {
    std::cout << std::any_cast<std::string>(a) << "\n";
}

// 使用 std::any_cast
try {
    int value = std::any_cast<int>(a);  // 抛出异常
} catch (const std::bad_any_cast& e) {
    std::cout << "Bad cast: " << e.what() << "\n";
}

std::string_view

#include <string_view>

// 避免字符串拷贝
void printString(std::string_view sv) {
    std::cout << sv << "\n";
}

std::string str = "hello";
const char* cstr = "world";

printString(str);      // OK
printString(cstr);     // OK
printString("test");   // OK

// 子串操作
std::string_view sv = "Hello, World!";
std::cout << sv.substr(0, 5) << "\n";  // Hello
std::cout << sv.starts_with("Hello") << "\n";  // true

std::byte

#include <cstddef>

// 字节类型,用于访问原始内存
std::byte b{42};

std::byte data[4];
data[0] = std::byte{0x12};
data[1] = std::byte{0x34};

// 转换为整数
int value = std::to_integer<int>(data[0]);  // 18

// 内存操作
std::memset(data, std::byte{0}, sizeof(data));

std::invoke

#include <functional>

// 调用函数指针
int add(int a, int b) { return a + b; }
int result = std::invoke(add, 3, 4);  // 7

// 调用成员函数
struct Calculator {
    int multiply(int a, int b) { return a * b; }
};
Calculator calc;
int product = std::invoke(&Calculator::multiply, calc, 3, 4);  // 12

// 调用 lambda
auto lambda = [](int a, int b) { return a * b; };
int lambdaResult = std::invoke(lambda, 5, 6);  // 30

std::apply

#include <tuple>

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

auto t = std::make_tuple(1, 2, 3);
int result = std::apply(add, t);  // 6

// 访问 tuple 元素
std::apply([](auto&... args) {
    ((std::cout << args << "\n"), ...);
}, t);

std::make_from_tuple

#include <tuple>

struct Point {
    int x;
    int y;
    int z;
};

auto t = std::make_tuple(1, 2, 3);
Point p = std::make_from_tuple<Point>(t);  // Point{1, 2, 3}

std::clamp

#include <algorithm>

int value = 75;
int minVal = 0;
int maxVal = 100;

int clamped = std::clamp(value, minVal, maxVal);  // 75
int clamped2 = std::clamp(-10, minVal, maxVal);   // 0
int clamped3 = std::clamp(150, minVal, maxVal);   // 100

// 带比较函数
auto clamped4 = std::clamp(value, minVal, maxVal, std::greater<int>());

std::reduce/std::transform_reduce

#include <numeric>
#include <execution>

std::vector<int> vec = {1, 2, 3, 4, 5};

// 并行归约
int sum = std::reduce(std::execution::par, vec.begin(), vec.end());

// 先转换后归约
auto squaredSum = std::transform_reduce(
    std::execution::par,
    vec.begin(), vec.end(),
    0LL,
    std::plus<>{},
    [](int x) { return x * x; }
);  // 55 (1+4+9+16+25)

std::filesystem

#include <filesystem>

namespace fs = std::filesystem;

// 创建目录
fs::create_directory("test");
fs::create_directories("a/b/c");  // 递归创建

// 遍历目录
for (const auto& entry : fs::directory_iterator(".")) {
    std::cout << entry.path() << "\n";
    std::cout << "Is file: " << entry.is_regular_file() << "\n";
}

// 检查文件
if (fs::exists("file.txt")) {
    std::cout << "File size: " << fs::file_size("file.txt") << "\n";
}

// 路径操作
fs::path p = "/home/user/documents/file.txt";
std::cout << "Filename: " << p.filename() << "\n";
std::cout << "Extension: " << p.extension() << "\n";
std::cout << "Parent: " << p.parent_path() << "\n";

// 相对路径
std::cout << "Relative: " << fs::relative(p) << "\n";

std::scoped_lock

#include <mutex>

std::mutex mtx1, mtx2;

// C++17 之前:手动 lock
std::lock(mtx1, mtx2);
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);

// C++17:scoped_lock 自动管理多个锁,完全替代 std::lock_guard
std::scoped_lock lock(mtx1, mtx2);
// 自动以避免死锁的方式获取所有锁
// 析构时自动释放

__has_include

#if __has_include(<optional>)
#include <optional>
#endif

#if __has_include(<filesystem>) && __has_include(<version>)
#include <filesystem>
#define HAS_FILESYSTEM 1
#else
#define HAS_FILESYSTEM 0
#endif

并行算法

#include <algorithm>
#include <execution>

std::vector<int> vec(1000000);
std::iota(vec.begin(), vec.end(), 0);

// 执行策略
std::sort(std::execution::par, vec.begin(), vec.end());  // 并行
std::sort(std::execution::seq, vec.begin(), vec.end());  // 顺序
std::sort(std::execution::par_unseq, vec.begin(), vec.end());  // 并行+向量化

// 并行查找
auto it = std::find(std::execution::par, vec.begin(), vec.end(), 42);

// 并行变换
std::transform(std::execution::par, vec.begin(), vec.end(), vec.begin(),
               [](int x) { return x * 2; });

std::shared_mutex

#include <shared_mutex>

class ThreadSafeCache {
    mutable std::shared_mutex mtx_;
    std::unordered_map<std::string, std::string> cache_;

public:
    // 读操作:使用共享锁
    std::string read(const std::string& key) const {
        std::shared_lock<std::shared_mutex> lock(mtx_);
        auto it = cache_.find(key);
        return it != cache_.end() ? it->second : "";
    }

    // 写操作:使用独占锁
    void write(const std::string& key, const std::string& value) {
        std::unique_lock<std::shared_mutex> lock(mtx_);
        cache_[key] = value;
    }
};

常用特性总结

最常用的 C++17 特性:

  • 结构化绑定
  • std::optional
  • std::variant
  • std::string_view
  • std::invoke
  • if 初始化语句
  • std::filesystem
  • [[nodiscard]] 属性