Item 2: 理解auto类型推导

理解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类型推导。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容