auto初始化表达式可以采用多种形式
- 通用初始化语法,如
auto a { 42 }
; 。 - 赋值语法,如
auto b = 0
; - 通用赋值语法,它结合了前两种形式,例如
auto c = { 3.14156 };
。 - 直接初始化或构造函数样式语法,如
auto d( 1.41421f );
#include <initializer_list>
using namespace std;
int main() {
auto A = {1, 2};
auto B = {3};
auto C{1};
auto D = {5, 6, 7};
auto E{8, 9}; // error: direct-list-initialization of 'auto' requires exactly one element [-fpermissive]
}
作用
自动类型推导
#include <iostream>
using namespace std;
int main()
{
auto num0 = 10;
cout << typeid(num0).name() << endl;
auto num1 = 10.1;
cout << typeid(num1).name() << endl;
auto num2 = 'c';
cout << typeid(num2).name() << endl;
auto num3 = "abc";
cout << typeid(num3).name() << endl; //char const *
auto num4 = L"abc";
cout << typeid(num4).name() << endl; //wchar_t const *
return 0;
}
总结:
- CPP中,auto是一种自适应类型,它会根据初始化的变量而自动推导类型,不能和int,double等关键字相结合。
- auto 让编译器通过初始值来进行类型推演。从而获得定义变量的类型,所以说auto定义的变量必须有初始值。
应用场景
- 当变量类型名很长时使用auto
- 自动推导类型用auto
- 当变量类型不确定时用auto
auto与指针
int main()
{
int* pp = new auto(); //错误 C2119 "new-expression": 无法从空的初始值设定项推导 "auto" 的类型
auto x = new auto();//错误 C2119 "new-expression": 无法从空的初始值设定项推导 "auto" 的类型
int* pp1 = new auto(1); // OK
auto x1 = new auto(11.1); // OK
auto* y = new auto(9); // OK
double a = 12.34;
auto *a1 = new auto(x), **a2 = new auto(&a);
return 0;
}
// 每个语句中的所有符号将解析为同一类型。
auto x = 1, *y = &x, **z = &y; // Resolves to int.
auto a(2.01), *b (&a); // Resolves to double.
auto c = 'a', *d(&c); // Resolves to char.
auto m = 1, &n = m; // Resolves to int.
auto与引用
auto与引用
#include <iostream>
using namespace std;
int main( )
{
int count = 10;
int& countRef = count;
auto myAuto = countRef;
countRef = 11;
cout << count << " ";
myAuto = 12;
cout << count << endl;
}
在上面的示例中,myAuto 是一个 int ,而不是一个 int 引用. 也就是说:auto将忽略引用
auto不能自动推导成CV-qualifiers(constant& volatile qualifiers),除非被声明为引用类型
#include <iostream>
using namespace std;
int main()
{
int a{ 10 };
cout << typeid(a).name() << endl; //int
int &b = a; //将a的值取出来放入b
b = 11;
cout << typeid(b).name() << endl; //int
//b是一个引用,去除引用
int &d = b;
d = 12;
cout << typeid(d).name() << endl; //int
auto c = b;
c = 12;
cout << typeid(c).name() << endl; //int
auto &e = b;
e = 13;
cout << typeid(e).name() << endl; //int
//b,c,e是同一块内存
}
#include <iostream>
using namespace std;
int main()
{
const int a1{ 10 };
//a1 = 12; //C3892 “a1” : 不能给常量赋值
cout << "const int a1 = "<< a1 << "\t"<< typeid(a1).name() << endl; //int
int b1 = a1;
const auto b2 = a1; //b是新内存,是const int *
b1 = 12;
// b2 = 12; //C3892 “b2” : 不能给常量赋值
cout << "int b1 = " << b1 << "\t" << typeid(b1).name() << endl; //int
auto c0 = a1; //只是将a1的值赋值过来,是int类型
c0 = 12;
cout << "auto c0 = " << c0 << "\t" << typeid(c0).name() << endl;
//int &c1 = a1; //C2440 “初始化” : 无法从“const int”转换为“int &” 、
auto &c1 = a1;//b和c是同一块内存
// c1 = 12; //C3892 “c1” : 不能给常量赋值
cout << "auto &c1 =" << c1 << "\t" << typeid(c1).name() << endl;
}
总结:如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。如果auto关键字带上&号,则不去除const语意。=是赋值,&是别名
使用auto时,当传递的是const变量的时候,必须手动加上const,因为auto不会自动推倒成const,除非和引用连用。
#include <iostream>
using namespace std;
int main()
{
const int a = 1;
const auto b = a; //b是const auto
auto c = a;//c的类型为int
auto& d = a;//d的类型为const int &
}
auto与三目运算符
int v1 = 100, v2 = 200;
auto x = v1 > v2 ? v1 : v2;
auto与函数
int f(int x) { return x; }
int main()
{
auto x = f(0);
const auto& y = f(1);
int (*p)(int x);
p = f;
auto fp = p;
//...
}
auto与数组
auto会退化成指向数组的指针,除非被声明为引用
#include <iostream>
using namespace std;
int main()
{
int a3[3] = { 1, 2, 3 };
auto b3 = a3;
cout << typeid(b3).name() << endl; //int *
int a7[3] = { 1, 2, 3 };
auto & b7 = a7;
cout << typeid(b7).name() << endl; //int []
}
auto与for循环
#include <deque>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
deque<double > dp(10, 0.1);
if(auto itr = std::find(dp.begin(), dp.end(), 0.1);itr != dp.end()){
*itr += 4;
}
for (auto iter = dp.begin(); iter != dp.end(); ++iter)
{ /* ... */ }
printf("\n");
for (auto elem : dp){
printf("%f\t", elem); // read only
}
printf("\n");
for (auto& elem : dp){
elem += 1; // writeable
}
for (const auto& elem : dp){
printf("%f\t", elem); // read only
}
}
#include <iostream>
using namespace std;
int main()
{
int a[5]{ 1, 2, 3, 4, 5 };
for (auto i : a) //auto 1是一个副本,会将a内存里面的值原样赋值
{
i += 1;
cout << i << "\t"; //2, --- 6
}
cout << "\n";
for (auto i : a) //auto 1是一个副本,会将a内存里面的值原样赋值
{
cout << i << "\t"; //1, 2, 3, 4, 5
}
}
总结:auto i是一个副本,会将数组元素原样赋值给i
如果要修改auto副本:for(auto &i : a),也就是声明为引用
#include <iostream>
using namespace std;
int main()
{
int a[5]{ 1, 2, 3, 4, 5 };
for (auto &i : a)
{
i += 1;
cout << i << "\t"; //2, --- 6
}
cout << "\n";
for (auto i : a)
{
cout << i << "\t"; //1, 2, 3, 4, 5
}
}
使用auto表示多维数组指针
多维数组指针
多维数组是一种常见的数据结构,实际上是数组的数组。这个含义虽然很好理解,但是在定义操作数组数据的指针时却有点麻烦。假设有下面的二维数组。
int matrix[10][10];
定义指向其中一行的指针时,下面那种方式是对的呢?
int *row[10];
int (*row)[10];
答案是第二种。这里硬记当然是一种办法,但是也可以用换一个方式看代码:
int* row[10];
int (*row)[10];
第一种情况是指针的数组,剩下的就是数组的指针了。
C++11的处理方式
C++11之后,有了auto描述符,有了begin、end函数,定义多维数组指针的时候就可以容易一些了,例如可以这样写代码:
int matrix[10][10];
int number = 1;
for(auto row = begin(matrix); row != end(matrix); ++row)
{
for(auto data = begin(*row); data != end(*row); ++data){
*data = number++;
}
}
因为row是数组指针,而begin和end的要求的是引用类型,所以在调用begin和end函数取得数据指针时使用的参数是*row,而不是row。
返回值占位
#include <iostream>
using namespace std;
using namespace std;
template <typename T1, typename T2>
auto compose1(T1 t1, T2 t2)
{
return t1 + t2;
}
template <typename T1, typename T2>
auto compose2(T1 t1, T2 t2) -> decltype(t1 + t2)
{
return t1 + t2;
}
int main()
{
cout << compose1(1, 11.1) << endl;
cout << compose2(1, 11.1) << endl;
}
总结:
函数模板中,与decltype结合表示函数返回的类型
- decltype操作符用于查询表达式的数据类型。
- auto在这里的作用也称为返回值占位,它只是为函数返回值占了一个位置,真正的返回值是后面的decltype(t1 + t2)
当模板函数的返回值依赖于模板的参数时,我们依旧无法在编译代码前确定模板参数的类型,故也无从知道返回值的类型,这时我们可以使用auto。
注意
auto是类型的占用符,但它本身不是类型。因此,不能用
sizeof(auto)
或者typeid(auto)
#include <iostream>
using namespace std;
int main()
{
cout << sizeof(auto) << endl;//错误
cout << typeid(auto).name() << endl;//错误
}
auto 关键字不能与任何其他类型说明符组合。
int main()
{
//错误原因: 因为变量 x 同时用 auto 关键字和类型声明 int
auto int x; // C3530: error: two or more data types in declaration of 'x'
return 0;
}
使用关键字声明的符号 auto 必须具有初始值设定项
int main()
{
auto i; //error: declaration of 'auto i' has no initializer
return 0;
}
auto 关键字不能声明数组
int main()
{
auto a[5]; // C3532
auto b[1][2]; // C3532
auto y[5] = x; // C3532
auto z[] = {1, 2, 3}; // C3532
auto w[] = x; // C3532
return 0;
}
不要将模板参数指定为 auto 关键字(考虑重载的问题,我们应该使用模板)。
#include <iostream>
using namespace std;
template<class T> class C{};
int main()
{
C<auto> c; // C3539
return 0;
}
//不能将变量强制转换为指定的类型**:
int main()
{
int value = 123;
auto(value); // C3537
(auto)value; // C3537
auto x1 = auto(value); // C3537
auto x2 = (auto)value; // C3537
auto x3 = static_cast<auto>(value); // C3537\
auto p = auto(3);
return 0;
}
在声明符列表中,“auto”必须始终推导为同一类型
// 每个语句声明多个变量,但关键字的每个使用都 auto 不能推导为同一类型。
int main()
{
// Variable x1 is a pointer to char, but y1 is a double.
auto * x1 = "a", y1 = 3.14;
// Variable c is a char and c1 is char*, but c2, and c3 are pointers to pointers.
auto c = 'a', *c1 = &c, * c2 = &c1, * c3 = &c2;
// Variable x2 is an int, but y2 is a double and z is a char.
auto x2(1), y2(0.0), z = 'a';
// Variable a is a pointer to int, but b is a pointer to double.
auto *a = new auto(1), *b = new auto(2.0);
return 0;
}
其他
void f(){}
int main()
{
auto x = f(); //因为初始化表达式的计算结果为 void
return 0;
}
// C3535b.cpp
// Compile with /Zc:auto
int main()
{
auto* x = 123.0; // C3535:因为该语句将变量声明 x 为指向推导出的类型的指针,而初始值设定项表达式的类型为 double。 因此,编译器无法推导出变量的类型。
return 0;
}
int main()
{
class A { };
A x;
auto *p = x; // C3535: 因为变量 p 声明指向推导出的类型的指针,但初始化表达式不是指针类型
return 0;
}
建议
建议你在 auto 大多数情况下使用关键字,除非你确实需要转换,因为它具有以下优势:
- 可靠性: 如果表达式的类型发生更改(这包括在函数返回类型发生更改时),则它将正常工作。
- 性能: 您可以保证不会进行任何转换。
- 可用性: 无需担心类型名称拼写错误和拼写错误。
- 效率: 你的编码可能更高效
http://c.biancheng.net/view/6984.html
https://docs.microsoft.com/zh-cn/cpp/cpp/auto-cpp