5

C++关键字系列(一)——auto 关键字

 8 months ago
source link: https://www.v2ex.com/t/1006059
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  C++

C++关键字系列(一)——auto 关键字

  johnsmith2077 · 2 小时 2 分钟前 · 237 次点击

最近看完了《 C++20 高级编程(第 5 版)》,想整理一下主要内容,就从关键字开始吧,既作为总结复习,也作为面试准备。主要内容由 GPT 生成,我个人负责审查内容和代码,介意者请关闭并拉黑。

注意:本文包含 AI 生成内容

在 C++中,auto关键字有两个主要的用途:自动类型推断和返回值占位符。

  • 自动类型推断🔍:auto可以根据初始化的值自动推断变量的类型。这在处理复杂类型,如 STL 容器的迭代器时,非常有用,可以使代码更加简洁。
std::vector<int> vec = {1, 2, 3, 4, 5};

// 使用 auto 关键字自动推断类型
for(auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << std::endl;
}

在这个例子中,auto被用来自动推断it的类型,它是std::vector<int>::iterator。没有auto,我们需要手动写出这个复杂的类型📚。

  • 返回值占位符🎯:在 C++14 以后,auto可以用作函数的返回值占位符,让编译器在编译时推断函数的返回类型。这在处理返回类型复杂或者依赖于模板参数的函数时非常有用。
template<typename T, typename U>
auto add(T t, U u) {
    return t + u;
}

int main() {
    auto result = add(1, 1.5);  // result 的类型被推断为 double
    std::cout << result << std::endl;
    return 0;
}

在这个例子中,auto被用作函数add的返回值类型🏷️。函数add可以接受任何类型的参数,返回值类型依赖于这些参数。由于参数类型在编译时才知道,所以我们使用auto让编译器在编译时推断返回值类型🔄。

  • range-based for 循环📝: auto关键字也可用于 range-based for 循环中,这样可以让编译器自动推断容器元素的类型🔎。例如:
std::vector<int> vec = {1, 2, 3, 4, 5};

for(auto x : vec) {
    std::cout << x << std::endl;
}

auto也可以搭配const/&/&&等修饰符使用,以避免拷贝或用于直接修改容器元素的值,例如:

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

for(const auto& x : vec) {
    std::cout << x << std::endl;
}
  • 结构化绑定🎁:在 C++17 以后,auto可以用于结构化绑定,这使得我们可以更方便地从复杂的数据结构中提取数据。例如👇:
std::map<std::string, int> map = {{"John", 1}, {"Mary", 2}};
for(auto [key, value] : map) {
    std::cout << key << ": " << value << std::endl;
}

在这个例子中,auto被用于结构化绑定,它可以自动推断keyvalue的类型,这是std::map<std::string, int>中的key_typemapped_type

当然,auto关键字在 C++中的用途不止于此🔍。它也可以用来构造泛型 lambda 表达式和简化函数模板👏。

  • 泛型 lambda 表达式📝:在 C++14 以后,auto可以用于 lambda 表达式的参数类型,这使得我们可以写出通用的 lambda 表达式🎉。
auto add = [](auto x, auto y) { return x + y; };

std::cout << add(1, 2) << std::endl;        // 输出 3
std::cout << add("Hello, "s, "World!") << std::endl; // 输出 Hello, World!

在这个例子中,add是一个通用的 lambda 表达式,它可以接受任何类型的参数,并返回它们的和💡。auto关键字使得我们可以在编译时推断参数的类型💼。

  • 简化函数模板 📝: 在 C++20 以后,auto可以用于普通函数的参数类型,这使得我们可以使用函数模板的简化语法。这是一个非常方便的特性,因为它可以让我们在编写函数模板时,无需显式声明模板参数。下面是一些示例:
auto add(auto a, auto b) {
    return a + b;
}

在这个例子中,add 函数可以接受任何类型的参数,只要这些类型支持 + 运算符。例如,我们可以这样调用它:

int main() {
    std::cout << add(1, 2) << std::endl; // 输出: 3
    std::cout << add(1, 2.5) << std::endl; // 输出: 3.5
    std::cout << add("John "s, "Smith") << std::endl; // 输出: John Smith
}
  • 简化函数模板与 Concepts📚:C++20 引入了 concepts ,这是一种表达模板参数要满足的条件的方法🎈。它允许我们在函数参数中直接使用 concepts 来约束参数的类型,这种特性使得我们可以更简洁地编写泛型代码✍️。

首先,我们需要定义一个 concept 。以下是一个简单的Incrementable的定义🧮:

template<typename T>
concept Incrementable = requires(T t) {
    { t++ } -> std::same_as<T>;
};

这个 concept 检查一个类型是否可以被(后缀)自增🔍。

然后,我们可以在函数参数中使用这个 concept 和auto来约束参数的类型🎯:

void increment(const Incrementable auto& t) {
    // ...
}

这个函数接受一个Incrementable类型的参数🔎。如果我们尝试传递一个不能自增的类型,编译器就会报错⚠️。

下面是一个完整的例子,包括一个接受Incrementable类型参数的函数和一些调用这个函数的代码📖:

#include <iostream>
#include <concepts>

template<typename T>
concept Incrementable = requires(T t) {
    { t++ } -> std::same_as<T>;
};

void increment(const Incrementable auto& t) {
    auto copy = t;
    copy++;
    std::cout << copy << std::endl;
}

int main() {
    int a = 5;
    increment(a); // 输出 6

    // std::string b = "hello";
    // increment(b); // 编译错误,std::string 不是 Incrementable
    return 0;
}

在这个例子中,increment函数接受一个Incrementable类型的参数,复制它,然后打印自增后的值🖨️。在main函数中,我们传递了一个整数给increment函数,这是一个Incrementable类型🎯。如果我们尝试取消注释并传递一个std::stringincrement函数,编译器就会报错,因为std::string不是Incrementable类型😵‍💫。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK