前言
编码时必须明确变量的类型会降低编码的效率,并使代码变得冗长。因此,C++提供了auto和decltype这两个关键字来实现类型的推导。
auto
基本语法
auto的基本语法如下:
1
| auto variable_name = expression;
|
编译器可以根据expression的类型来自动推导出variable_name的类型。
使用示例
auto比较常见的用法是推导迭代起来类型的推导:
1 2 3 4 5 6 7 8
| int x = 5; auto a = x;
double d = 3.14; auto b = d;
std::vector<int> vec = {1, 2, 3}; auto it = vec.begin();
|
注意事项
1 2 3 4 5 6
| const int ci = 42; auto a = ci;
int x = 10; int& r = x; auto b = r;
|
如果想要保留修饰符,需要手动加上:
1 2 3
| auto& c = r;
const auto d = ci;
|
C++14用法增强
1 2 3
| auto add(int a, int b) { return a + b; }
|
1 2 3 4 5 6
| auto func = [](auto x, auto y) { return x + y; }
func(1, 2); func(1.5, 2.5);
|
C++20用法增强
1 2 3 4
| int add(auto x, auto y) { return x + y; } std::cout << add(5, 6) << std::endl;
|
万能引用(转发引用)
auto&&是C++11引入的一种万能引用,可以根据初始化表达式的值类型(左值/右值)推导出不同的类型。给定如下代码,编译器会根据expr的值类型,推导出x的类型:
通过使用auto&&,可以绑定左值也可以绑定右值的引用类型:
1 2 3
| int a = 42; auto&& x = a; auto&& y = 123;
|
使用场景
1 2 3 4
| template<typename T> void wrapper(T&& arg) { func(std::forward<T>(arg)); }
|
其中 T&& 是模板参数的万能引用,auto&& 是其非模板变量形式,两者原理相似。
1 2 3 4 5 6
| std::vector<std::string> v = {"hello", "world"};
for (auto&& s : v) { std::cout << s << std::endl; }
|
如果你不确定容器中元素是左值还是右值、是否能被修改,auto&& 是一种保险、灵活又高效的选择。
decltype
基本语法
auto关键字只能对变量类型进行推导,为了解决这一缺陷,引入了decltype,其用法如下:
使用示例
1 2 3
| auto x = 1; auto y = 2; decltype(x+y) z;
|
尾返回类型推导
能否直接使用decltype推导函数的返回类型呢?就像下面的代码这样:
1 2 3 4
| template<typename T, typename U> decltype(x, y) add(T x, U y) { return x + y; }
|
答案是不能的,这样写无法通过编译。因为编译器在读到decltype(x+y)时,x和y还没有被定义。为此,C++11引入了尾返回类型,利用auto关键字返回类型后置:
1 2 3 4
| template<typename T, typename U> auto add(T x, U y) -> decltype(x + y) { return x + y; }
|
其中,auto是用于配合尾返回类型的占位符,->decltype(x + y)表示返回类型是表达式x + y的类型。这样写的好处是即使在模板定义中的T和U类型不同,也能正确推导出返回值的实际类型。例如int + double会返回double。
C++14开始可以直接让普通函数具备返回值推导,因此下面的写法变得合法:
1 2 3 4
| template<typename T, typename U> auto add2(T x, U y){ return x + y; }
|
decltype(auto)
decltype(auto)是C++14开始提供的用法,其主要用于对转发函数或封装的返回类型进行推导,而无需显示地指定。例如,对于下面的两个函数:
1 2
| std::string lookup1(); std::string& lookup2();
|
在C++中,封装实现的形式如下:
1 2 3 4 5 6
| std::string look_up_a_string_1() { return lookup1(); } std::string& look_up_a_string_2() { return lookup2(); }
|
而使用decltype(auto)则可以让编译器完成参数的转发,无需显示指定参数类型:
1 2 3 4 5 6
| decltype(auto) look_up_a_string_1() { return lookup1(); } decltype(auto) look_up_a_string_2() { return lookup2(); }
|
参考
Modern C++