C++关键字系列(一)auto 关键字 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
johnsmith2077
V2EX    C++

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

  •  
  •   johnsmith2077 2024-01-05 10:39:53 +08:00 1785 次点击
    这是一个创建于 647 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近看完了《 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  
       2024-01-05 10:46:19 +08:00   1
    ...为什么加这么多没用的符号在文章里
    iOCZS
        2
    iOCZS  
       2024-01-05 10:49:28 +08:00
    返回值占位符如果在 c++11 中的话,还得配合 decltype
    johnsmith2077
       3
    johnsmith2077  
    OP
       2024-01-05 10:56:54 +08:00
    @mainjzb 我本人有点阅读障碍,长段的文字很难集中注意力,以至于只能逐字阅读,加上 emoji 我个人感觉会易读一点,主要还是起到分隔文本的作用,如果反响太差的话之后可以考虑去掉
    johnsmith2077
        4
    johnsmith2077  
    OP
       2024-01-05 11:31:57 +08:00
    @iOCZS 是的,在 c++11 中需要使用尾置返回类型,形如:auto add(T1 t1, T2 t2) -> decltype(t1+t2),Scott Meyers 的 Effective 系列也有提到,具体不记得是哪一本了
    proxytoworld
        5
    proxytoworld  
       2024-01-05 12:03:39 +08:00
    技术性文章还是少一点 emoji ,毕竟不是发在小红书
    constexpr
        6
    constexpr  
       2024-01-05 12:11:39 +08:00
    非常期待 constexpr 关键字的讲解
    jancing
        7
    jancing  
       2024-01-05 12:35:22 +08:00 via Android   1
    这本书原版要出第六版了
    Professional C++ (Tech Today) https://a.co/d/64UFcVR
    guazao
        8
    guazao  
       2024-01-05 13:00:30 +08:00
    auto 可以推出来 cv 跟引用吗?
    johnsmith2077
        9
    johnsmith2077  
    OP
       2024-01-05 13:47:27 +08:00
    @guazao 单独使用 auto 会忽略 cv 和引用,如果需要完整保留 cv 和引用,可以使用 decltype(auto)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5916 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 06:11 PVG 14:11 LAX 23:11 JFK 02:11
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86