auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。
使用auto会拖慢c++效率吗?完全不会,因为在编译阶段编译器已经帮程序员推导好了变量的类型。
使用auto会拖累C++编译效率吗?完全不会,因为在auto出现之前C++需要先推导等号右侧表达式的类型,然后检查它与变量的类型是否可以转换(兼容转换、向下类型转换和自定义类型转换)。auto出现之后,C++在推导出等号右侧表达式的类型之后,直接指定给变量。
一 使用auto注意事项
- auto 定义变量时==必须同时初始化==(类似引用)
因为是根据变量的值进行类型推导
#include <iostream>
int main()
{
auto x; // error
auto y = 10; // ok
}
编译错误
➜ main make lan=c++ ver=c++11
g++ main.cpp -std=c++11
main.cpp:4:8: error: declaration of variable 'x' with type 'auto' requires an initializer
auto x;
^
1 error generated.
make: *** [all] Error 1
➜ main
requires an initializer auto x;
- 不允许使用auto定义==函数参数==
#include <iostream>
void func(auto x)
{}
int main()
{}
编译错误
➜ main make lan=c++ ver=c++11
g++ main.cpp -std=c++11
main.cpp:3:11: error: 'auto' not allowed in function prototype
void func(auto x)
^~~~
1 error generated.
make: *** [all] Error 1
➜ main
- 不允许使用auto定义==struct/class的成员变量==
#include <iostream>
struct animal
{
int age;
auto name; // error: 无法确定最终外层struct的占用长度
};
int main()
{}
编译错误
➜ main make lan=c++ ver=c++11
g++ main.cpp -std=c++11
main.cpp:6:3: error: 'auto' not allowed in non-static struct member
auto name;
^~~~
1 error generated.
make: *** [all] Error 1
➜ main
- 不允许使用auto定义==数组==
#include <iostream>
int main()
{
auto arr[5]; // error: 同样无法确定数组的总占用长度
}
编译错误
➜ main make lan=c++ ver=c++11
g++ main.cpp -std=c++11
main.cpp:5:11: error: 'arr' declared as array of 'auto'
auto arr[5];
^
1 error generated.
make: *** [all] Error 1
➜ main
- 不允许使用auto作为==模板参数==传递
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v1; // ok
std::vector<auto> v2; // error
}
编译错误
➜ main make lan=c++ ver=c++11
g++ main.cpp -std=c++11
main.cpp:7:15: error: 'auto' not allowed in template argument
std::vector<auto> v2; // error
^~~~
main.cpp:7:21: error: C++ requires a type specifier for all declarations
std::vector<auto> v2; // error
^
2 errors generated.
make: *** [all] Error 1
➜ main
二 函数返回值推导
函数返回类型自动推导
函数返回类型自动推导是指C++11以及C++14中不直接给出函数返回类型,而是使用类型指示符来指示返回类型甚至彻底省略返回类型并最终由编译器来推导返回类型的语言特性。
函数返回类型自动推导原则如下:
(1)当函数体内不包含任何返回值时,该函数的返回类型为void。
当函数体内只包含一句带返回值的return语句时,该函数的返回类型等同于该返回值的类型。
(2)当函数体内包含多个带返回值的return语句时,该函数的返回类型由这些返回值的类型共同决定,所有返回值的类型必须相同。
函数体内的返回值可以递归调用自身,但这类无法由编译器自动推导类型的返回值,必须发生在可以由编译器自动推导类型的非递归调用的返回值之后。
(3)在lambda表达式中省略返回类型
(4)当lambda表达式中省略返回类型时,lambda表达式的返回类型由编译器根据返回值以及模板参数推导规则进行自动推导。
声明函数时使用auto指示符来指示返回类型
当函数的返回类型包含auto指示符并且函数的后置返回类型被省略时,函数的返回类型由编译器根据返回值以及模板参数推导规则进行自动推导。
声明函数时使用decltype(auto)指示符来指示返回类型
当函数的返回类型为decltype(auto)指示符时,函数的返回类型由编译器根据返回值以及decltype推导规则进行自动推导。
#include <iostream>
#include <string>
#include <typeinfo>
template<typename T>
T f();
auto f2(int n)
{
return n + 1;
}
auto f3(bool b)
{
if(b) return 1.0;
else return 2.0;
}
auto f4(int n)
{
if(n == 0) return 1;
else return n * f4(n - 1);
}
struct S
{
int n1, n2;
decltype(auto) f6(bool b)
{
if(b) return (n1);
else return (n2);
}
};
int main()
{
auto f1 = [](int a){std::cout << a;};
auto f5 = [](auto a, auto b){return a + b;};
f<decltype(f1(1))>();
f<decltype(f2(1))>();
f<decltype(f3(false))>();
f<decltype(f4(3))>();
f<decltype(f5(1.0f, 3.0f))>();
S s;
f<decltype(s.f6(false))>();
}
main.cpp:(.text.startup+0x5): undefined reference to `void f<void>()'
main.cpp:(.text.startup+0xa): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0xf): undefined reference to `double f<double>()'
main.cpp:(.text.startup+0x14): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0x19): undefined reference to `float f<float>()'
main.cpp:(.text.startup+0x1e): undefined reference to `int& f<int&>()'
这段代码使用了一种特殊技巧来查看编译器所实际推导出的函数返回类型。代码中定义了一个未被实现的函数模板f,该函数模板有一个类型参数T。如果用某个类型T来调用该函数模板,编译器就会因找不到函数模板的定义而输出”T f<T>()未被定义“的出错信息。也就是说,利用该函数模板,我们便可以在编译器所输出的出错信息中检查模板参数T的实际类型。在main函数内,我们充分利用了这一个技巧,通过利用函数返回类型调用该函数模板来让编译器输出相应的类型。
从出错信息中可以得知:
lambda表达式f1的返回类型被推导为void。
理由:lambda表达式的函数体内没有任何返回值。
函数f2的返回类型被推导为int。
理由:函数体内只包含一个return语句,返回值n+1类型为int。
函数f3的返回类型被推导为double。
理由:函数体内包含两个return语句,返回值1.0以及2.0类型都是double。
函数f4的返回类型被推导为int。
理由:函数体内包含两个return语句,其中第二个返回值递归调用自身,无法推导,而第一个返回值1类型为int。
lambda表达式f5的返回类型被推导为表达式a+b的类型。当 a=1.0f, b=3.0f 时,f5的返回类型为表达式 1.0f + 3.0f 的类型,即float。
理由:函数体内只包含一个return语句,返回值a+b类型取决于泛型参数a以及b的类型。
函数f6的返回类型被推导为int&。
理由:函数体内包含两个return语句,将返回值(n1)以及(n2)代入decltype(auto)结果类型都是int&。