C++ auto 关键字

68 阅读1分钟

使用 auto 关键字自动推导变量类型(C++11)

C++11 允许使用 auto 关键字对变量类型进行自动推导,通常用在变量类型较长或者很难写出变量类型的场景。

来看一个例子:

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>int main() {
    std::unordered_map<std::string, std::vector<int>> str2vec;
    str2vec["hello"] = std::vector<int>{1, 2, 3, 4, 5};
    str2vec["world"] = std::vector<int>{6, 7, 8, 9, 10};
    // 迭代器的类型为 std::unordered_map<std::string, std::vector<int>>::const_iterator,非常的长,可读性差
    for (std::unordered_map<std::string, std::vector<int>>::const_iterator it = str2vec.begin(); it != str2vec.end(); ++it) {
        std::cout << it->first << ' ' << it->second[0] << std::endl;
    }
    // 使用 auto 自动推导迭代器的类型,方便且可读性好
    for (auto it = str2vec.begin(); it != str2vec.end(); ++it) {
        std::cout << it->first << ' ' << it->second[0] << std::endl;
    }
    return 0;
}

运行结果:

linrongjian@hhb-ssd:/data/modern_cpp$ ./auto_example
world 6
hello 1
world 6
hello 1

可以看到效果是一样的,显然使用 auto 关键字推导复杂类型要比显示地写出来方便的多,而且很多时候我们没法准确写出来变量的类型(一些复杂类型在不同编译器下可能会生成不同的类型),这种情况下就可以用 auto 关键字,让编译器去帮我们做这个 dirty work

再提一嘴,上面的例子可以将 auto 关键字和基于范围的 for 循环(C++11)以及结构化绑定(C++17)结合:

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>int main() {
    std::unordered_map<std::string, std::vector<int>> str2vec;
    str2vec["hello"] = std::vector<int>{1, 2, 3, 4, 5};
    str2vec["world"] = std::vector<int>{6, 7, 8, 9, 10};
    // much simpler
    for (const auto& [k, v] : str2vec) {
        std::cout << k << ' ' << v[0] << std::endl;
    }
    return 0;
}

什么时候使用 auto

  1. 一般一眼可以变量的类型时可以用 auto

    比如基本类型。

  2. 复杂类型、lambda表达式、std::bind 等也可以用 auto

    • 复杂类型可以用 auto 来推导类型,代码也会更简洁。
    • 对于 lambdastd::bind,在不同编译器下可能会产生不同的类型,很难准确写出来其类型,因此使用 auto,把这个工作交给编译器。

使用 auto 推导函数返回类型(C++14)

可以使用 auto 关键字自动推导函数的返回值类型,只要确保即使在有多重返回值时函数的返回值类型依旧是确定的即可(也就是不存在可能返回类型 A 也可能返回类型 B 的情况)。

例子:

#include <iostream>auto sum(int a, int b) {
    return a + b;
}
​
int main() {
    std::cout << sum(1, 2) << std::endl;
}

因为返回值类型确定是 int,所以可以正确推导:

linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++14 auto_function_return_type.cpp -o auto_function_return_type
linrongjian@hhb-ssd:/data/modern_cpp$ ./auto_function_return_type
3

在 lambda 表达式中使用 auto(C++14)

  1. C++14 允许把 lambda 表达式的形参声明为 auto,这样就得到了一个泛型的 lambda 表达式。

    例子:

    #include <iostream>
    #include <assert.h>auto sum = [](auto a, auto b) {
        return a + b;
    };
    ​
    int main() {
        std::cout << sum(1, 2.0) << std::endl;
        assert(sum(1, 2.0) == 3.0);
    }
    

    虽然输出显示的是 3,但从 assert 可以看出,这里返回值被推导为更强的 double 类型:

    linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++14 auto_lambda.cpp -o auto_lambda
    linrongjian@hhb-ssd:/data/modern_cpp$ ./auto_lambda
    3
    
  2. 还可以对 lambda 表达式返回一个 auto 引用。

    例子:

    #include <iostream>
    #include <assert.h>
    ​
    auto get_ref = [](auto& a) -> auto& {
        return a;
    };
    ​
    int main() {
        auto var1 = 5;
        auto& var2 = get_ref(var1);
        assert(&var1 == &var2);
    }
    

    var2 是一个 var1 的引用,众所周知引用和原变量指向的是相同的内存地址。

非类型模板形参占位符(C++17)

C++17 运行 auto 关键字作为非类型模板形参的占位符。

例子:

#include <iostream>
​
template <auto N>
void f() {
    std::cout << N << std::endl;
}
​
int main() {
    f<5>();
    f<'c'>();
}

运行结果:

linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++17 auto_template_placeholder.cpp -o auto_template_placeholder
linrongjian@hhb-ssd:/data/modern_cpp$ ./auto_template_placeholder
5
c

注意,这里对模板形参 N 的类型是有要求的,用错类型会导致编译错误。

根据 C++ Templates,非类型模板参数对形参的要求:

  1. 字符串不可以作为非类型形参。
  2. intcharlongunsignedboolshort 等可以被转为整数的类型可。
  3. 指向对象或函数的指针与引用(左值引用)可以作为形参。

参考资料

  1. 现代C++语言核心特性解析
  2. C++ Templates