最近看完了《 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
,我们需要手动写出这个复杂的类型📚。
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
让编译器在编译时推断返回值类型🔄。
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;
}
auto
可以用于结构化绑定,这使得我们可以更方便地从复杂的数据结构中提取数据。例如👇:std::map<std::string, int> map = {{"John", 1}, {"Mary", 2}};
for(auto [key, value] : map) {
std::cout << key << ": " << value << std::endl;
}
在这个例子中,auto
被用于结构化绑定,它可以自动推断key
和value
的类型,这是std::map<std::string, int>
中的key_type
和mapped_type
。
当然,auto
关键字在 C++中的用途不止于此🔍。它也可以用来构造泛型 lambda 表达式和简化函数模板👏。
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
关键字使得我们可以在编译时推断参数的类型💼。
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
}
首先,我们需要定义一个 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::string
给increment
函数,编译器就会报错,因为std::string
不是Incrementable
类型😵💫。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.