理解auto类型推导
除了一个例外,auto 类型推断与模板类型推断相同。
模板类型推导和 auto 类型推导之间有直接的映射,从字面上看,有一种算法上的转换。
// 模板类型推导
template<typename T>
void f(ParamType param);
f(expr); // 以 expr 表达式为参数调用f
使用 auto 声明变量时,auto 在模板中扮演 T 的角色,并且变量的类型说明符充当 ParamType 。
auto x = 27;// x 的类型为 auto
const auto cx = x;// cx 的类型为 const auto
const auto& rx = x;// rx 的类型为 const auto&.
// 编译器将此理解为,每个声明都有一个模板,以及使用相应的初始化表达式对该模板的调用一样:
template<typename T>
void func_for_x(T param); // 推导 x 类型的概念模板
func_for_x(27); // 概念调用:param 的推导类型为 x 的类型
template<typename T>
void func_for_cx(const T param); // 推导 cx 类型的概念模板
func_for_cx(x); // 概念调用:param 的推导类型为 cx 的类型
template<typename T>
void func_for_rx(const T& param);// 推导 rx 类型的概念模板
func_for_rx(x);// 概念调用:param 的推导类型为 rx 的类型
//注意,这个 auto 的类型,还需根据 param 的类型进行推导。
根据通用函数模板中 param 的类型说明符 ParamType 的特征,item 1 将模板类型推导分为三种情况。 在使用 auto 的变量声明中,变量的类型说明符代替了ParamType,因此也有以下三种情况:
Case 1: 类型说明符是一个指针或者引用,但不是万能引用。
Case 2: 类型说明符是一个万能引用。
Case 3: 类型说明符既不是指针,也不是引用。
auto x = 27; //case 3
const auto cx = x; //case 3
const auto& rx = x; //case 1
//Case 2
auto&& uref1 = x; // x is int and lvalue,
// so uref1's type is int&
auto&& uref2 = cx; // cx is const int and lvalue,
// so uref2's type is const int&
auto&& uref3 = 27; // 27 is int and rvalue,
// so uref3's type is int&&
Item 1 讨论了数组和函数名如何转化为非引用类型说明符的指针。 auto 类型推导也会发生这种情况:
const char name[] = // name's type is const char[13]
"R. N. Briggs";
auto arr1 = name; // arr1's type is const char*
auto& arr2 = name; // arr2's type is
// const char (&)[13]
void someFunc(int, double); // someFunc is a function;
// type is void(int, double)
auto func1 = someFunc; // func1's type is
// void (*)(int, double)
auto& func2 = someFunc; // func2's type is
// void (&)(int, double)
auto 类型推导和模板类型推导的不同在于:
auto 和模板类型推导之间的唯一真正区别是 auto 假定大括号初始值设代表std :: initializer_list,而模板类型推导则不。
//C++ 98
int x1 = 27;
int x2(27);
//C++ 11 统一初始化/标准初始化 uniform initialization
int x3 = { 27 };
int x4{ 27 };
// Item 5解释说,使用auto而不是固定类型声明变量有很多优点,因此最好在上述变量声明中将int替换为auto。
auto x1 = 27;// 类型为 int, 值为27
auto x2(27);// 同上
auto x3 = { 27 };// 类型为std::initializer_list<int>, 值为 { 27 }
auto x4{ 27 };// 同上
这是由于 auto 的特殊类型推导规则。 当自动声明的变量的初始化程序放在大括号中时,推导的类型为std :: initializer_list。 如果无法推断出此类类型(例如,因为括号初始化器中的值属于不同类型),则该代码将报错:
// 因为大括号初始化(braced initializer) 中的值是不同类型的。
auto x5 = { 1, 2, 3.0 }; // 错误! 不能推导出 std::initializer_list<T> 中 T 的类型
// 在这种情况下,有两种类型推导发生。
// 因为 x5 是大括号初始化,X5 推导为std::initializer_list.
//std::initializer_list 又是一个模板,std :: initializer_list <T> 的实例化,这意味着还必须推导T的类型。
对于大括号初始化的处理是 auto 类型推导和模板类型推导唯一不同。 当使用大括号初始化器初始化 auto 声明的变量时,推导的类型为std :: initializer_list的实例化。 但是,如果通过相同的初始器传递给相应的模板,则类型推导将失败,并且代码将报错:
auto x = { 11, 23, 9 }; // x 的类型为 std::initializer_list<int>
//与x的声明等效的含参模板
template<typename T>
void f(T param);
f({ 11, 23, 9 }); // 错误! 不能推导出 T 的类型
但是,如果您在模板中为某些未知的T指定param是std :: initializer_list <T>,则模板类型推导将得出 T 是什么类型:
template<typename T>
void f(std::initializer_list<T> initList);
f({ 11, 23, 9 });
// T推导为int,并且initList的类型为std::initializer_list <int>
原版英文如下:
C++14 permits auto to indicate that a function’s return type should be deduced (see Item 3), and C++14 lambdas may use auto in parameter declarations. However, these uses of auto employ template type deduction, not auto type deduction. So a function with an auto return type that returns a braced initializer won’t compile:
一言以蔽之:
auto in a function return type or a lambda parameter implies template type deduction, not auto type deduction.
中文翻译:
函数返回类型或lambda参数中的 auto 采用模板类型推导,而不是auto类型推导。
auto createInitList()
{
return { 1, 2, 3 }; // 错误!不能推导出{1,2,3}的类型
}
在C++ 14 lambda的参数类型声明中使用auto时,也是如此:
std::vector<int> v;
…
auto resetV =
[&v](const auto& newValue) { v = newValue; }; // C++14
…
resetV({ 1, 2, 3 }); // 错误!不能推导出{1,2,3}的类型
总结
auto 类型推导通常与模板类型推导相同,但是 auto 类型推导假定大括号初始值设定项器代表std :: initializer_list,而模板类型推导则不。
函数返回类型或lambda参数中的 auto 采用模板类型推导,而不是auto类型推导。