auto
C++是一种静态强类型语言,任何一个变量都要有确定的类型,否则不能用。所以编译器必须知道类型,使用auto就是让编译器告诉类型
auto必须要有表达式才能推导,并且推导也不是完全正确。比如auto s = "hello";
推导出来的是const char *,而不是string
所以auto用于初始化的场合,就是赋值初始化或者列表初始化,变量右边必须要有一个表达式。
特殊情况,在类成员变量初始化的时候,目前的 C++ 标准不允许使用 auto 推导类型
auto基本的规则:
- auto 总是推导出“值类型”,绝不会是“引用”;
- auto 可以附加上 const、volatile、*、& 这样的类型修饰符,得到新的类型
- auto不能保留const属性
const int a = 100;
auto b = a;
b = 1000;
std::cout << b; //1000,b已经被改变,不是常量了
int a = 100;
int& la = a;
auto b = la; //b为int,去掉了引用属性
b=200;
std::cout<<a; // 这里a输出仍为100,证明b不是引用
auto x = 10L; // auto推导为long,x是long
auto& x1 = x; // auto推导为long,x1是long&
auto* x2 = &x; // auto推导为long,x2是long*
const auto& x3 = x; // auto推导为long,x3是const long&
auto x4 = &x3; // auto推导为const long*,x4是const long*
因为auto会去掉引用属性,所以在声明函数的时候,可以使用拖尾函数指明auto推导的类型
int& bigger(int& a, int& b)
{
return a > b ? a : b;
}
//拖尾函数
auto bigger(int& a, int& b)->int&
{
return a > b ? a : b;
}
//因为函数返回引用类型,所以直接将大的那个数改为500,如果使用auto bigger(int& a, int& b)就不行
int a = 100;
int b = 50;
bigger(a, b) = 500;
std::cout << a << std::endl; // 500
decltype
因为auto必须需要表达式,而没有表达式的时候,就要用decltype,也就是自己把表达式带上
decltype 的形式很像函数,后面的圆括号里就是可用于计算类型的表达式(和 sizeof 有点类似),其他方面就和 auto 一样了,也能加上 const、*、& 来修饰。但因为它已经自带表达式,所以不需要变量后面再有表达式,也就是说可以直接声明变量。
int x = 0; // 整型变量
decltype(x) x1; // 推导为int,x1是int
decltype(x)& x2 = x; // 推导为int,x2是int&,引用必须赋值
decltype(x)* x3; // 推导为int,x3是int*
decltype(&x) x4; // 推导为int*,x4是int*
decltype(&x)* x5; // 推导为int*,x5是int**
decltype(x2) x6 = x2; // 推导为int&,x6是int&,引用必须赋值
auto与decltype的区别:decltype 不仅能够推导出值类型,还能推导出引用类型,也能保留const属性,就是表达式的“原始类型”。
除了加上 * 和 & 修饰,decltype 还可以直接从一个引用类型的变量推导出引用类型(上面的x6),而 auto 就会把引用去掉,推导出值类型。所以,完全可以把 decltype 看成是一个真正的类型名,用在变量声明、函数参数 / 返回值、模板参数等任何类型能出现的地方,只不过这个类型是在编译阶段通过表达式“计算”得到的。
decltype规则:
- 如果表达式没经历运算,则推断类型和表达式完全一样(包括const,引用等)
- 如果经历了运算,根据运算结果是否有固定地址(左值)来确定。有固定地址,推断为引用类型;没有则推断为运算结果类型
- 表达式为函数,根据函数返回类型决定,但不会真的执行函数,而auto会去执行函数
int a = 100;
unsigned b = 50;
int* pa{ &a };
decltype(a+b) x; // x为unsigned,因为a+b是临时变量,没有固定地址(右值),a+b运算结果是unsigned
decltype(*pa) x=a; // x为int&,*pa运算结果有固定地址,推断为引用类型,引用类型必须赋初值
decltype(*pa + 1) x; //x为int,*pa+1为右值
decltype(pa[0]) x=a; //x为int&,pa[0]有固定地址,即使pa[2]也为int&,因为pa[2]也有固定地址,只是不能访问
decltype((a)) x=a; // x为int&,()也是一次运算
//证明上述第三点,结果只输出auto调用,证明decltype不会执行函数
int func1(int a)
{
std::cout << "auto调用" << std::endl;
return a;
}
int func2(int a)
{
std::cout << "decltype调用" << std::endl;
return a;
}
int main()
{
auto autox = func1(100);
decltype(func2(100)) declx;
return 0;
}
C++14 增加了一个“decltype(auto)”的形式,既可以精确推导类型,又能像 auto 一样方便使用。C++14还新增了一个应用场合,就是使用auto能够推导函数返回值,这样在写复杂函数的时候,比如返回一个 pair、容器或者迭代器,就会很省事。
decltype 是 auto 的高级形式,更侧重于编译阶段的类型计算,所以常用在泛型编程里,获取各种类型,配合 typedef 或者 using 会更加方便。
//C++11的写法
//auto bigger(int& a, int& b)->decltype(a > b ? a : b)
//{
// return a > b ? a : b;
//}
// C++14的写法
decltype(auto) bigger(int& a, int& b)
{
return a > b ? a : b;
}
int main()
{
int a = 100, b = 200;
bigger(a, b)=400;
std::cout << b; //400
return 0;
}
注意的问题:
template<typename T1, typename T2>
decltype(auto) bigger(T1 &a, T2& b)
{
return a > b ? a : b;
}
int main()
{
float a = 50.2f;
int b = 500;
bigger(a, b); //推断出的返回类型为float,注意不是float的引用
// 函数在比较a和b大小的时候,做了隐式类型转换,将b转成了float,因此返回的是临时变量(右值),所以是值类型
// 隐式类型转换产生了一个临时变量来存储转换后的值
//例如:
float& x = (float)b; //此处会报错初始值必须是左值,说明隐式类型转换时是右值
}
以上就是auto和decltye常见用法