V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
johnsmith2077
V2EX  ›  C++

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

  •  
  •   johnsmith2077 · 308 天前 · 1337 次点击
    这是一个创建于 308 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近看完了《 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类型😵‍💫。

    9 条回复    2024-01-05 13:47:27 +08:00
    mainjzb
        1
    mainjzb  
       308 天前   ❤️ 1
    ...为什么加这么多没用的符号在文章里
    iOCZS
        2
    iOCZS  
       308 天前
    返回值占位符如果在 c++11 中的话,还得配合 decltype
    johnsmith2077
        3
    johnsmith2077  
    OP
       308 天前
    @mainjzb 我本人有点阅读障碍,长段的文字很难集中注意力,以至于只能逐字阅读,加上 emoji 我个人感觉会易读一点,主要还是起到分隔文本的作用,如果反响太差的话之后可以考虑去掉😂
    johnsmith2077
        4
    johnsmith2077  
    OP
       308 天前
    @iOCZS 是的,在 c++11 中需要使用尾置返回类型,形如:auto add(T1 t1, T2 t2) -> decltype(t1+t2),Scott Meyers 的 Effective 系列也有提到,具体不记得是哪一本了
    proxytoworld
        5
    proxytoworld  
       308 天前
    技术性文章还是少一点 emoji ,毕竟不是发在小红书
    constexpr
        6
    constexpr  
       308 天前
    非常期待 constexpr 关键字的讲解
    jancing
        7
    jancing  
       308 天前 via Android   ❤️ 1
    这本书原版要出第六版了
    Professional C++ (Tech Today) https://a.co/d/64UFcVR
    guazao
        8
    guazao  
       308 天前
    auto 可以推出来 cv 跟引用吗?
    johnsmith2077
        9
    johnsmith2077  
    OP
       308 天前
    @guazao 单独使用 auto 会忽略 cv 和引用,如果需要完整保留 cv 和引用,可以使用 decltype(auto)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2554 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 15:26 · PVG 23:26 · LAX 07:26 · JFK 10:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.