关键字
auto
语法
(1) (C++11 起) decltype(auto)
(2) (C++14 起) 类型制约 auto
(3) (C++20 起) 类型制约 decltype(auto)
(4) (C++20 起) 类型制约 - 概念名,可以有限定,可以后随 <> 包围的模板实参列表 1,3) 用模板实参推导的规则推导类型。2,4) 类型为 decltype(expr),其中 expr 是初始化器。占位符 auto 可伴随如 const 或 & 这样的修饰符,它们参与类型推导。占位符 decltype(auto) 必须是被声明类型的唯一组分。 (C++14 起)
解释
这里尽量不提及C++11以后的标准
变量的类型说明符中:auto x = expr;。从初始化器推导类型。若占位符类型说明符为 auto 或 类型制约 auto (C++20 起),则采用从函数调用进行模板实参推导的规则,从初始化器推导变量的类型(细节见其他语境)。例如,给定 const auto& i = expr;,则 i 的类型恰是某个虚构模板 template<class U> void f(const U& u) 中参数 u 的类型(假如函数调用 f(expr) 通过编译)。因此,取决于初始化器,auto&& 可被推导成左值引用或右值引用类型,这被用于基于范围的 for 循环。
new 表达式中的类型标识。从初始化器推导类型。对于 new T init(其中 T 含占位符类型,而 init 是带括号的初始化器或带花括号的初始化器列表),如同在虚设的声明 T x init; 中对变量 x 一般推导 T 的类型。
用法
C++11中,auto不再是一个存储类型指示符,而是一个自动推导变量的类型。auto并非是一种类型的声明,而是一个类型声明时的“占位符”,编译器在编译期间会将auto替换为变量实际的类型。
我觉得auto给我们带来的最大改变有两方面。
-
解放程序员双手。有了auto这个神器,妈妈再也不用担心我的键盘手了。auto可以代替以前要打的很长很长的变量类型。
-
还是用在模板推导,下边的例子如果没有auto,x*y就没有办法填写类型了。
template <typename _Tx,typename _Ty>
void Multiply(_Tx x, _Ty y)
{
auto v = x*y;
std::cout << v;
}
需要注意的是:
-
auto 变量必须在定义时初始化,这类似于const关键字。
-
如果初始化表达式是引用,则去除引用语义。
-
如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。
-
如果auto关键字带上&号,则不去除const语意。
const int a2 = 10;
auto &b2 = a2;//因为auto带上&,故不去除const,b2类型为const int
-
初始化表达式为数组时,auto关键字推导类型为指针。
-
若表达式为数组且auto带上&,则推导类型为数组类型。
-
函数或者模板参数不能被声明为auto
void func(auto a) //错误
{
//...
}
-
时刻要注意auto并不是一个真正的类型。auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid。
cout << sizeof(auto) << endl;//错误
cout << typeid(auto).name() << endl;//错误
例子
#include <iostream>
#include <utility>
template<class T, class U>
auto add(T t, U u) { return t + u; } // 返回类型是 operator+(T, U) 的类型
// 在其所调用的函数返回引用的情况下
// 函数调用的完美转发必须用 decltype(auto)
template<class F, class... Args>
decltype(auto) PerfectForward(F fun, Args&&... args)
{
return fun(std::forward<Args>(args)...);
}
template<auto n> // C++17 auto 形参声明
auto f() -> std::pair<decltype(n), decltype(n)> // auto 不能从花括号初始化器列表推导
{
return {n, n};
}
int main()
{
auto a = 1 + 2; // a 的类型是 int
auto b = add(1, 1.2); // b 的类型是 double
static_assert(std::is_same_v<decltype(a), int>);
static_assert(std::is_same_v<decltype(b), double>);
auto c0 = a; // c0 的类型是 int,保有 a 的副本
decltype(auto) c1 = a; // c1 的类型是 int,保有 a 的副本
decltype(auto) c2 = (a); // c2 的类型是 int&,为 a 的别名
std::cout << "a, before modification through c2 = " << a << '\n';
++c2;
std::cout << "a, after modification through c2 = " << a << '\n';
auto [v, w] = f<0>(); // 结构化绑定声明
auto d = {1, 2}; // OK:d 的类型是 std::initializer_list<int>
auto n = {5}; // OK:n 的类型是 std::initializer_list<int>
// auto e{1, 2}; // C++17 起错误,之前为 std::initializer_list<int>
auto m{5}; // OK:C++17 起 m 的类型为 int,之前为 initializer_list<int>
// decltype(auto) z = { 1, 2 } // 错误:{1, 2} 不是表达式
// auto 常用于无名类型,例如 lambda 表达式的类型
auto lambda = [](int x) { return x + 3; };
// auto int x; // 于 C++98 合法,C++11 起错误
// auto x; // 于 C 合法,于 C++ 错误
}
可能的输出:
a, before modification through c2 = 3
a, after modification through c2 = 4
关注公众号获取更多信息: