C++17基础语法完整教程

C++17 是 C++ 编程语言的一个重要版本,它在 C++14 的基础上引入了许多新特性,旨在简化代码、提高性能并增强语言的表达能力。本教程将全面介绍 C++17 的基础语法,帮助你快速掌握这一现代 C++ 版本的核心特性。

一、C++17 新特性概述

C++17 引入了许多新特性,可以分为以下几类:

  1. ​结构化绑定(Structured Bindings)​
  2. ​if 和 switch 中的初始化语句​
  3. ​内联变量(Inline Variables)​
  4. ​折叠表达式(Fold Expressions)​
  5. std::optional 和 std::variant
  6. std::string_view
  7. ​并行算法(Parallel Algorithms)​
  8. ​文件系统库(Filesystem Library)​
  9. ​其他改进和优化​

下面我们将逐一详细介绍这些特性。

二、结构化绑定(Structured Bindings)

结构化绑定允许你将一个复合类型(如 std::pairstd::tuple 或数组)的成员绑定到多个变量上,从而简化代码。

示例代码
#include <iostream>
#include <tuple>

int main() {
    // 使用 std::tuple
    std::tuple<int, double, std::string> t = {1, 3.14, "Hello"};
    auto [id, pi, message] = t;  // 结构化绑定
    
    std::cout << "ID: " << id << ", Pi: " << pi << ", Message: " << message << std::endl;

    // 使用数组
    int arr[3] = {10, 20, 30};
    auto [a, b, c] = arr;  // 结构化绑定
    
    std::cout << "Array elements: "<< a << ", "<< b << ", "<< c << std::endl;

    return 0;
}

解释:​

  • 结构化绑定使得我们可以直接从 std::tuple 或数组中提取值并赋给多个变量,而不需要手动访问每个成员。
  • 这种语法糖让代码更加简洁易读。

三、if 和 switch 中的初始化语句

C++17 允许在 if 和 switch 语句中进行变量声明和初始化,这可以减少代码的嵌套层次,提高可读性。

示例代码

#include <iostream>
#include <optional>

std::optional<int> findValue(const std::vector<int>& vec, int target) {
    for (int v : vec) {
        if (v == target) return v;
    }
    return std::nullopt;
}

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

    // if 中的初始化语句
    if (auto value = findValue(numbers, 3); value.has_value()) {
        std::cout << "Found: " << *value << std::endl;
    } else {
        std::cout << "Not found" << std::endl;
    }

    // switch 中的初始化语句(C++17 不支持直接在 switch 中初始化,但可以通过作用域实现)
    {
        auto value = findValue(numbers, 6);
        switch (value.has_value()) {
            case true:
                std::cout << "Found: " << *value << std::endl;
                break;
            case false:
                std::cout << "Not found" << std::endl;
                break;
        }
    }

    return 0;
}

解释:​

  • 在 if 语句中,我们可以在条件部分声明并初始化一个变量 value,这使得代码更加紧凑。
  • 虽然 switch 语句不支持直接在条件部分初始化变量,但我们可以通过作用域来模拟类似的效果。

四、内联变量(Inline Variables)

内联变量允许你在头文件中定义变量,而不会导致多重定义错误。这在 C++17 之前需要使用 static 或在源文件中定义变量。

示例代码

// my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <string>

inline const std::string appName = "MyApp";

#endif // MY_HEADER_H

// main.cpp
#include <iostream>
#include "my_header.h"

int main() {
    std::cout << "Application Name: " << appName << std::endl;
    return 0;
}

解释:​

  • 在 C++17 之前,如果你在头文件中定义一个变量,然后在多个源文件中包含该头文件,会导致链接错误(多重定义)。
  • 使用 inline 关键字,你可以在头文件中安全地定义变量,编译器会确保变量只被定义一次。

五、折叠表达式(Fold Expressions)

折叠表达式简化了可变参数模板的展开操作,使得代码更加简洁。

示例代码

#include <iostream>

template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // 折叠表达式
}

int main() {
    std::cout << "Sum of 1, 2, 3, 4, 5: " << sum(1, 2, 3, 4, 5) << std::endl;
    return 0;
}

解释:​

  • 折叠表达式 (args + ...) 将可变参数 args 展开为一个加法表达式。
  • 这种语法糖使得处理可变参数模板变得更加简单。

六、std::optional 和 std::variant

1. std::optional

std::optional 用于表示一个可能包含值或不包含值的对象,避免了使用特殊值(如 -1nullptr)来表示无效状态。

示例代码

#include <iostream>
#include <optional>

std::optional<int> divide(int a, int b) {
    if (b == 0) {
        return std::nullopt;  // 表示没有值
    }
    return a / b;
}

int main() {
    auto result = divide(10, 2);
    if (result) {
        std::cout << "Result: " << *result << std::endl;
    } else {
        std::cout << "Division by zero" << std::endl;
    }

    return 0;
}

解释:​

  • std::optional 提供了一种类型安全的方式来表示可能缺失的值。
  • 使用 std::nullopt 表示没有值,通过 has_value() 或直接检查 optional 对象是否为空来判断是否有值。

2. std::variant

std::variant 是一个类型安全的联合体,可以存储多种类型的值,类似于 union,但更加安全。

示例代码

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

int main() {
    std::variant<int, double, std::string> v;

    v = 42;
    std::cout << "Holds int: " << std::get<int>(v) << std::endl;

    v = 3.14;
    std::cout << "Holds double: " << std::get<double>(v) << std::endl;

    v = "Hello";
    std::cout << "Holds string: " << std::get<std::string>(v) << std::endl;

    return 0;
}

解释:​

  • std::variant 可以存储多种类型的值,但同一时间只能存储其中一种类型。
  • 使用 std::get<T> 来获取存储的值,如果类型不匹配会抛出异常。

七、std::string_view

std::string_view 提供了一个轻量级的、非拥有的字符串视图,避免了不必要的字符串拷贝。

示例代码

#include <iostream>
#include <string_view>

void printString(std::string_view sv) {
    std::cout << sv << std::endl;
}

int main() {
    std::string str = "Hello, World!";
    printString(str);  // 传递 std::string

    printString("Direct string literal");  // 传递字符串字面量

    return 0;
}

解释:​

  • std::string_view 不拥有字符串数据,只是提供一个视图,因此避免了拷贝开销。
  • 可以接受 std::string 和字符串字面量作为参数。

八、并行算法(Parallel Algorithms)

C++17 引入了并行版本的 STL 算法,可以显著提高计算密集型任务的性能。

示例代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>

int main() {
    std::vector<int> vec(1000000, 1);

    // 并行执行 std::for_each
    std::for_each(std::execution::par, vec.begin(), vec.end(), [](int& n) {
        n += 1;
    });

    // 并行执行 std::sort
    std::sort(std::execution::par, vec.begin(), vec.end());

    std::cout << "First element: " << vec[0] << std::endl;
    return 0;
}

解释:​

  • 使用 std::execution::par 策略,算法会并行执行,充分利用多核处理器。
  • 注意:并行算法需要编译器支持,并且可能需要链接额外的库(如 TBB)。

九、文件系统库(Filesystem Library)

C++17 引入了 <filesystem> 库,提供了跨平台的文件系统操作功能。

示例代码

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
    fs::path p = "/path/to/directory";

    if (fs::exists(p)) {
        std::cout << "Path exists" << std::endl;
        
        if (fs::is_directory(p)) {
            std::cout << "It's a directory" << std::endl;
            
            for (const auto& entry : fs::directory_iterator(p)) {
                std::cout << entry.path().filename() << std::endl;
            }
        }
    } else {
        std::cout << "Path does not exist" << std::endl;
    }

    return 0;
}

解释:​

  • <filesystem> 库提供了跨平台的文件系统操作,如检查文件是否存在、遍历目录等。
  • 使用 namespace fs = std::filesystem 可以简化代码。

十、其他改进

1. 结构化绑定与 std::pair 和 std::tuple

#include <iostream>
#include <tuple>

int main() {
    auto [x, y, z] = std::make_tuple(1, 2.5, "Hello");
    std::cout<< x << ", "<< y << ", "<< z << std::endl;
    return 0;
}

2. if 和 switch 中的初始化语句

#include <iostream>
#include <optional>

std::optional<int> findValue(const std::vector<int>& vec, int target) {
    for (int v : vec) {
        if (v == target) return v;
    }
    return std::nullopt;
}

int main() {
    std::vector<int> v = {1, 2, 3};
    
    if (auto it = findValue(v, 2); it) {
        std::cout << "Found: " << *it << std::endl;
    } else {
        std::cout << "Not found" << std::endl;
    }
    
    return 0;
}

3. [[nodiscard]] 属性

#include <iostream>

[[nodiscard]] int computeValue() {
    return 42;
}

int main() {
    // 编译器警告:忽略返回值
    computeValue();
    
    // 正确用法
    int value = computeValue();
    std::cout << "Value: " << value << std::endl;
    
    return 0;
}

解释:​

  • [[nodiscard]] 属性告诉编译器,函数的返回值不应该被忽略。
  • 如果调用者忽略了返回值,编译器会发出警告。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_shenbing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值