强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

C/C++ Linux 开发教程(GCC + CMake) / 现代 C++(C++11/14/17/20)

现代 C++(C++11/14/17/20)

1. auto 类型推断(C++11)

auto 让编译器自动推导变量类型,减少冗余代码。

#include <iostream>
#include <vector>
#include <map>
#include <string>

int main() {
    // 基本用法
    auto i = 42;              // int
    auto d = 3.14;            // double
    auto s = std::string("hello"); // std::string
    auto p = &i;              // int*

    std::cout << "i=" << i << " d=" << d << " s=" << s << "\n";

    // 容器迭代器(不再需要写冗长的类型名)
    std::map<std::string, std::vector<int>> data;
    data["scores"] = {90, 85, 95};

    for (auto it = data.begin(); it != data.end(); ++it) {
        std::cout << it->first << ": ";
        for (auto val : it->second) {
            std::cout << val << " ";
        }
        std::cout << "\n";
    }

    // C++14: auto 函数返回类型
    auto multiply = [](auto a, auto b) { return a * b; };
    std::cout << multiply(3, 4) << "\n";    // 12
    std::cout << multiply(2.5, 4.0) << "\n"; // 10.0

    // 注意:auto 不适合需要明确类型的场景
    auto x = {1, 2, 3};  // std::initializer_list<int>,不是 std::vector!

    return 0;
}

⚠️ 注意auto x = {1, 2, 3} 推导为 std::initializer_list<int>,而非 std::vector<int>


2. 范围 for 循环(C++11)

#include <iostream>
#include <vector>
#include <string>
#include <map>

int main() {
    // 基本遍历
    std::vector<int> nums = {1, 2, 3, 4, 5};

    for (auto n : nums) {              // 值拷贝
        std::cout << n << " ";
    }
    std::cout << "\n";

    for (const auto& n : nums) {       // const 引用(推荐只读遍历)
        std::cout << n << " ";
    }
    std::cout << "\n";

    for (auto& n : nums) {             // 可变引用
        n *= 2;
    }

    for (auto&& n : nums) {            // 万能引用(通用写法)
        std::cout << n << " ";
    }
    std::cout << "\n";

    // 初始化列表
    for (auto v : {10, 20, 30, 40, 50}) {
        std::cout << v << " ";
    }
    std::cout << "\n";

    // map 遍历(结构化绑定)
    std::map<std::string, int> ages = {{"Alice", 25}, {"Bob", 30}};
    for (const auto& [name, age] : ages) {
        std::cout << name << " is " << age << "\n";
    }

    // C++20:初始化语句
    std::vector values = {1, 2, 3, 4, 5};
    // for (auto i = 0; auto& v : values) {  // C++20
    //     std::cout << i++ << ": " << v << "\n";
    // }

    return 0;
}

3. Lambda 表达式

#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

int main() {
    std::vector<int> v = {5, 3, 1, 4, 2};

    // C++11 基本 Lambda
    std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

    // C++14 泛型 Lambda
    auto add = [](auto a, auto b) { return a + b; };
    std::cout << add(3, 4) << "\n";       // 7
    std::cout << add(1.5, 2.5) << "\n";   // 4.0

    // C++14 初始化捕获(移动捕获)
    auto ptr = std::make_unique<int>(42);
    auto lambda = [p = std::move(ptr)]() {
        std::cout << "值: " << *p << "\n";
    };
    lambda();

    // C++17 constexpr Lambda
    constexpr auto square = [](int n) { return n * n; };
    static_assert(square(5) == 25);

    // C++20 模板 Lambda
    // auto print_anything = []<typename T>(const T& v) {
    //     std::cout << v << "\n";
    // };

    // C++20 初始化捕获带包展开
    // auto factory = [args...]<typename T>() { return T(args...); };

    // Lambda 作为回调
    std::function<int(int, int)> ops[] = {
        [](int a, int b) { return a + b; },
        [](int a, int b) { return a * b; },
        [](int a, int b) { return a - b; }
    };

    for (auto& op : ops) {
        std::cout << op(10, 3) << " ";  // 13 30 7
    }
    std::cout << "\n";

    // IIFE(立即调用 Lambda 表达式)
    auto result = [](int x) {
        return x * x + 2 * x + 1;
    }(5);
    std::cout << "f(5) = " << result << "\n";  // 36

    return 0;
}

4. 初始化列表 initializer_list(C++11)

#include <iostream>
#include <vector>
#include <string>
#include <initializer_list>

class Config {
    std::string name_;
    std::vector<std::string> values_;

public:
    // 接受初始化列表的构造函数
    Config(const std::string& name, std::initializer_list<std::string> values)
        : name_(name), values_(values) {}

    void print() const {
        std::cout << name_ << ": ";
        for (const auto& v : values_) {
            std::cout << "[" << v << "] ";
        }
        std::cout << "\n";
    }
};

// 统一初始化语法
struct Point {
    double x;
    double y;
    double z;
};

int main() {
    // 统一初始化(花括号初始化)
    int a{42};
    double b{3.14};
    std::string s{"hello"};
    std::vector<int> v{1, 2, 3, 4, 5};

    // 聚合初始化
    Point p{1.0, 2.0, 3.0};
    std::cout << "p = (" << p.x << ", " << p.y << ", " << p.z << ")\n";

    // 自定义类使用初始化列表
    Config config("server", {"localhost", "8080", "4"});
    config.print();

    // 不允许窄化转换
    // int x{3.14};  // 编译警告/错误:窄化转换

    // 空初始化 = 零初始化
    int zero{};     // 0
    double dz{};    // 0.0
    std::string es{};  // ""

    std::cout << "zero=" << zero << " dz=" << dz << " es=\"" << es << "\"\n";

    return 0;
}

5. 右值引用与移动语义(C++11)

#include <iostream>
#include <string>
#include <vector>
#include <utility>

class Buffer {
    int* data_;
    size_t size_;

public:
    explicit Buffer(size_t n) : data_(new int[n]), size_(n) {
        std::cout << "构造: " << size_ << " 元素\n";
    }

    ~Buffer() { delete[] data_; }

    // 拷贝构造(深拷贝)
    Buffer(const Buffer& other) : data_(new int[other.size_]), size_(other.size_) {
        std::copy(other.data_, other.data_ + size_, data_);
        std::cout << "拷贝: " << size_ << " 元素\n";
    }

    // 移动构造(窃取资源)
    Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
        std::cout << "移动: " << size_ << " 元素\n";
    }

    // 移动赋值
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        std::cout << "移动赋值\n";
        return *this;
    }

    size_t size() const { return size_; }
};

Buffer createBuffer(size_t n) {
    return Buffer(n);  // NRVO 可能优化掉移动
}

int main() {
    Buffer a(1000);           // 构造
    Buffer b = a;             // 拷贝构造
    Buffer c = std::move(a);  // 移动构造(a 被"掏空")
    Buffer d = createBuffer(500);  // 可能直接构造(NRVO)

    std::cout << "a.size=" << a.size() << " (已移动)\n";

    return 0;
}

左值 vs 右值

类别说明示例
左值 (lvalue)有名字、有地址、可取地址变量、*pa[i]、赋值表达式
右值 (rvalue)临时对象、字面量、std::move 返回值42std::string("hi")std::move(x)
将亡值 (xvalue)即将被移动的左值std::move(x) 的返回值

6. 结构化绑定(Structured Bindings, C++17)

#include <iostream>
#include <tuple>
#include <map>
#include <string>
#include <array>

struct Person {
    std::string name;
    int age;
    std::string city;
};

std::tuple<int, double, std::string> getData() {
    return {42, 3.14, "hello"};
}

int main() {
    // tuple 解构
    auto [id, value, label] = getData();
    std::cout << "id=" << id << " value=" << value << " label=" << label << "\n";

    // pair 解构
    std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
    for (const auto& [name, score] : scores) {
        std::cout << name << ": " << score << "\n";
    }

    // 数组解构
    int arr[] = {10, 20, 30};
    auto [x, y, z] = arr;
    std::cout << x << ", " << y << ", " << z << "\n";

    // 结构体解构
    Person p{"Alice", 25, "Beijing"};
    auto [name, age, city] = p;
    std::cout << name << ", " << age << "岁, " << city << "\n";

    // 嵌套结构化绑定
    std::map<std::string, std::pair<int, int>> stats = {
        {"A", {100, 200}},
        {"B", {300, 400}}
    };
    for (const auto& [key, [min_val, max_val]] : stats) {
        std::cout << key << ": [" << min_val << ", " << max_val << "]\n";
    }

    // if 初始化语句 (C++17)
    if (auto [iter, success] = scores.insert({"Charlie", 92}); success) {
        std::cout << "插入成功: " << iter->first << "\n";
    }

    return 0;
}

7. std::optional / variant / any(C++17)

std::optional

#include <iostream>
#include <optional>
#include <string>

std::optional<int> parse_int(const std::string& s) {
    try {
        return std::stoi(s);
    } catch (...) {
        return std::nullopt;
    }
}

std::optional<std::string> find_env(const char* name) {
    const char* val = std::getenv(name);
    if (val) return std::string(val);
    return std::nullopt;
}

int main() {
    auto result = parse_int("42");
    if (result) {
        std::cout << "解析成功: " << *result << "\n";
    }

    auto bad = parse_int("abc");
    std::cout << "has_value: " << std::boolalpha << bad.has_value() << "\n";

    // value_or 提供默认值
    int val = parse_int("xyz").value_or(-1);
    std::cout << "默认值: " << val << "\n";

    // optional 用于可能失败的操作
    auto env = find_env("HOME");
    if (env) {
        std::cout << "HOME=" << *env << "\n";
    }

    return 0;
}

std::variant

#include <iostream>
#include <variant>
#include <string>
#include <vector>

using Value = std::variant<int, double, std::string>;

void print(const Value& v) {
    std::visit([](const auto& val) {
        std::cout << val << " ";
    }, v);
}

int main() {
    std::vector<Value> data;
    data.push_back(42);
    data.push_back(3.14);
    data.push_back(std::string("hello"));

    for (const auto& v : data) {
        print(v);
    }
    std::cout << "\n";

    // 获取值
    Value v = 42;
    std::cout << "int: " << std::get<int>(v) << "\n";

    // 安全获取
    if (auto* p = std::get_if<int>(&v)) {
        std::cout << "是 int: " << *p << "\n";
    } else {
        std::cout << "不是 int\n";
    }

    // index()
    std::cout << "类型索引: " << v.index() << "\n";  // 0 = int

    return 0;
}

std::any

#include <iostream>
#include <any>
#include <string>

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

    a = std::string("hello");
    std::cout << "string: " << std::any_cast<std::string>(a) << "\n";

    // 类型检查
    std::cout << "type: " << a.type().name() << "\n";

    // 安全转换(失败抛异常)
    try {
        auto val = std::any_cast<int>(a);  // a 是 string,会抛异常
    } catch (const std::bad_any_cast& e) {
        std::cerr << "转换失败: " << e.what() << "\n";
    }

    return 0;
}
工具类型安全适用场景
optional<T>固定类型值可能存在或不存在
variant<Ts...>编译期类型集合类型安全的联合体
any运行时类型擦除完全动态类型

8. 文件系统 std::filesystem(C++17)

#include <iostream>
#include <filesystem>
#include <fstream>

namespace fs = std::filesystem;

int main() {
    // 创建临时目录
    fs::path dir = "/tmp/cpp_demo";
    fs::create_directories(dir);

    // 创建文件
    std::ofstream(dir / "file1.txt") << "Hello";
    std::ofstream(dir / "file2.txt") << "World";
    fs::create_directories(dir / "subdir");

    // 遍历目录
    std::cout << "目录内容:\n";
    for (const auto& entry : fs::directory_iterator(dir)) {
        std::cout << "  " << entry.path().filename()
                  << (entry.is_directory() ? "/" : "")
                  << (entry.is_regular_file() ?
                      " (" + std::to_string(entry.file_size()) + " bytes)" : "")
                  << "\n";
    }

    // 递归遍历
    std::cout << "\n递归遍历:\n";
    for (const auto& entry : fs::recursive_directory_iterator(dir)) {
        std::cout << "  " << fs::relative(entry.path(), dir) << "\n";
    }

    // 路径操作
    fs::path p = "/home/user/documents/report.pdf";
    std::cout << "\n路径分解:\n";
    std::cout << "  root: " << p.root_path() << "\n";
    std::cout << "  parent: " << p.parent_path() << "\n";
    std::cout << "  filename: " << p.filename() << "\n";
    std::cout << "  stem: " << p.stem() << "\n";
    std::cout << "  extension: " << p.extension() << "\n";

    // 文件状态
    auto status = fs::status(dir);
    std::cout << "\n目录状态: " << static_cast<int>(status.type()) << "\n";

    // 磁盘空间
    auto space = fs::space("/");
    std::cout << "可用空间: " << space.available / (1024 * 1024 * 1024) << " GB\n";

    // 清理
    fs::remove_all(dir);
    std::cout << "已清理临时目录\n";

    return 0;
}

编译需要链接 -lstdc++fs(GCC < 9):

g++ -std=c++17 -o demo demo.cpp && ./demo

9. 格式化 std::format(C++20)

#include <iostream>
#include <format>
#include <string>

int main() {
    // 基本格式化
    std::string s = std::format("Hello, {}!", "World");
    std::cout << s << "\n";

    // 数字格式化
    std::cout << std::format("整数: {:10d}\n", 42);       // 右对齐,宽度10
    std::cout << std::format("填充: {:*<10d}\n", 42);     // 左对齐,* 填充
    std::cout << std::format("居中: {:*^10d}\n", 42);     // 居中
    std::cout << std::format("浮点: {:.3f}\n", 3.14159); // 3位小数
    std::cout << std::format("科学: {:.2e}\n", 1234.5);  // 科学记数法
    std::cout << std::format("十六: {:08x}\n", 255);     // 十六进制,前导零
    std::cout << std::format("二进: {:08b}\n", 42);      // 二进制

    // 位置参数
    std::cout << std::format("{1} 说 {0}\n", "你好", "Alice");

    // 自定义类型格式化
    struct Point {
        double x, y;
    };

    // 需要特化 std::formatter(此处仅展示概念)

    return 0;
}

⚠️ 注意:GCC 13+ 完整支持 std::format。较早版本可使用 {fmt} 库作为替代。


10. 协程简介(C++20)

#include <iostream>
#include <coroutine>
#include <optional>

// 简单的生成器(Generator)
template <typename T>
struct Generator {
    struct promise_type {
        T current_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 value) {
            current_value = std::move(value);
            return {};
        }

        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

    std::coroutine_handle<promise_type> handle;

    ~Generator() {
        if (handle) handle.destroy();
    }

    // 获取下一个值
    std::optional<T> next() {
        if (!handle || handle.done()) return std::nullopt;
        handle.resume();
        if (handle.done()) return std::nullopt;
        return handle.promise().current_value;
    }
};

// 协程函数:无限斐波那契生成器
Generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;  // 暂停并返回值
        auto temp = a + b;
        a = b;
        b = temp;
    }
}

// 协程函数:范围生成器
Generator<int> range(int start, int end, int step = 1) {
    for (int i = start; i < end; i += step) {
        co_yield i;
    }
}

int main() {
    // 使用斐波那契生成器
    auto fib = fibonacci();
    std::cout << "斐波那契前 10 个: ";
    for (int i = 0; i < 10; ++i) {
        if (auto val = fib.next()) {
            std::cout << *val << " ";
        }
    }
    std::cout << "\n";

    // 使用范围生成器
    auto r = range(0, 20, 3);
    std::cout << "range(0,20,3): ";
    while (auto val = r.next()) {
        std::cout << *val << " ";
    }
    std::cout << "\n";

    return 0;
}

编译需要 GCC 10+ 或 Clang 14+:

g++ -std=c++20 -fcoroutines -o demo demo.cpp && ./demo

⚠️ 注意co_awaitco_yieldco_return 是协程关键字。GCC 10+ 用 -fcoroutines 启用。


11. 模块简介(C++20)

// math_utils.cppm(模块接口文件)
// export module math_utils;
//
// export int add(int a, int b) { return a + b; }
// export int multiply(int a, int b) { return a * b; }
//
// export template <typename T>
// T max_val(T a, T b) { return a > b ? a : b; }

// main.cpp(使用模块)
// import math_utils;
// import <iostream>;
//
// int main() {
//     std::cout << add(3, 4) << "\n";
//     std::cout << multiply(5, 6) << "\n";
//     return 0;
// }

模块编译示意:

# GCC 编译模块(实验性支持)
# g++ -std=c++20 -fmodules-ts -c math_utils.cppm -o math_utils.gcm
# g++ -std=c++20 -fmodules-ts main.cpp math_utils.gcm -o demo

💡 提示:GCC 对 C++20 模块的支持仍在完善中。生产环境建议继续使用头文件 + #pragma once。Clang 和 MSVC 的模块支持更为成熟。


C++ 标准演进速查表

标准年份关键特性
C++112011auto, lambda, 右值引用, 智能指针, 范围 for
C++142014泛型 lambda, 返回类型推导, 二进制字面量
C++172017结构化绑定, optional/variant/any, filesystem, constexpr if
C++202020Concepts, Ranges, Coroutines, Modules, format
C++232023expected, print, mdspan, stacktrace

扩展阅读