前置文章 型别推导
采用auto进行变量声明时候,型别饰词取代ParamType。所以也存在三种情况:
- 情况1:型别饰词是指针或者引用,但不是万能引用。
- 情况2:型别饰词是万能引用。
- 情况3:型别饰词不是指针也不是引用。
int x = 27;
const int cx = x;
auto&& uref1 = x; // x 型别为int,且为左值
// 所以 uref1的型别是int&
auto&& uref2 = cx; // cx 是const int,且左值, 所以 uref2是const int&
// 结论情况2时,如果是auto万能引用,且那个是左值时,都推导为左值引用
auto&& uref3 = 27; // 27型别为右值, 所以uref3的型别为int&&
同样的数组实参和函数实参的退化在非引用型别饰词的退化成指针的情况也同样适用于auto型别推导。
const char ch[] = "Hello World!"; // ch的型别是const char[13] 也就是 const T (&)[N]型别。
auto ch1 = ch; // ch1的型别是 const char*
auto ch2 = ch; // ch2的型别是 const char (&)[13]
void someFunc(int, double); // someFunc是一个函数,型别是 void(int, double)
auto func1 = someFunc; // func1的型别是void (*)(int, double)
auto func2 = someFunc; // func2的型别是void (&)(int, double)
auto型别推导和模板型别推导运作相似,只有一处不同。
int x1 = 27; // 型别int, 值为27
int x2(27); // 型别int, 值为27
int x3 = {27}; // 型别int, 值为27
int x4{27}; // 型别int, 值为27
上面都是得到一个值为27的int,但是如果把上述的int换成auto就会有点不一样
auto x1 = 27; // 型别int, 值为27
auto x2(27); // 型别int, 值为27
auto x3 = {27};// 型别std::initializer_list<int>, 值为27
auto x4{27};// 型别std::initializer_list<int>, 值为27
结论
如果用auto声明变量的初始化表达式是使用{ }
,那么所得的型别就是std::initalizer_list
。
但是如果
{}
内的型别是不一样的,者代码编译不通过。
auto x5 = {1, 2, 3.0 }; //编译错误
auto与模板型别推导区别在于,auto会假定大括号的初始化表达式代表一个std::initializer_list
,但是模板推导不会。
auto x = {1, 2, 3}; // x的型别是std::initializer_list<int>
template<typename T>
void f(T param);
f({11, 23, 9}); // 错误,无法推导T的型别
但是如果指定模板中的param为std::initilaizer_list 就可以推导T的型别
template <typename T>
void f(std::initilaizer_list<T> param);
f({1, 2, 3}); // T为int
结论:只有必要的时候才会初始化表达式时使用大括号。
在lambda表达式中看是包含auto可以进行推导,实际上是模板推导的情况。
模板推导就不会推导带{}
初始化变量的情况。
auto createInitList() {
return {1, 2, 3}; // 错误: 无法进行模板推导
}
std:: vector<int > v;
auto resetV = [&v](const auto& newValue){ v = newValue; };
resetV({1, 2, 3}); //错误, 无法为{1, 2, 3}进行型别推导。
要点
- 一般情况下auto型别推导和模板型别推导是一样的。但是在使用auto型别推导时会假定大括号括起来的初始化表达式为一个std::initializer_list
而模板推导不会这样做。 - 在函数返回或者lambda式的形参使用auto,意思是模板型别推导,而不是auto型别推导。