08 | 易用性改进 I:自动类型推断和初始化
08 | 易用性改进 I:自动类型推断和初始化
讲述:吴咏炜
时长15:24大小14.12M
自动类型推断
auto
decltype
decltype(auto)
函数返回值类型推断
类模板的模板参数推导
结构化绑定
列表初始化
统一初始化
类数据成员的默认初始化
内容小结
课后思考
参考资料
赞 7
提建议
精选留言(31)
- robonix2020-01-06老师,文中提到auto&& a = expr; 推断结果是一个跟 expr 值类别相同的引用类型。那么如果expr是左值引用或右值引用,对应的推断类型又是啥呢?
作者回复: 仍然是第3讲里的规则,左值得到左值引用,右值得到右值引用(但要注意右值引用是个左值): int x = 42; int& a = x; int&& b = 42; auto&& c = a; // int& auto&& d = b; // int& auto&& e = std::move(b); // int&&
共 2 条评论15 - 中年男子2019-12-14建议各位如果文章中有没看懂的地方,去看看老师在文末的参考资料,这些也都是好东西
作者回复: 识货。😇 毕竟这个专栏的篇幅是 30 讲,不是 60 讲或 100 讲啊。
9 - 花晨少年2019-12-14如果一个类有使用初始化列表的构造函数时,则只应用在初始化列表构造的情况。 是说{1.0}这种形式只用在初始化列表构造的情况吗? 什么是初始化列表构造的情况?不明白
作者回复: 是说如果一个类Obj既有: Obj(initializer_list<int>); 又有: Obj(double); 那你想调用后面那个构造函数,就别用 Obj{1.0} 这种形式,而用 Obj(1.0)。
6 - Geek_68d3d22020-06-11在使用模板的时候为什么T是容器的时候前面要加上一个typename?? 比如typename T::const_iterator it = v.begin()
作者回复: 语言规则要求。在编译器不知道T是啥(模板定义中,尚未实例化)的情况下,显式通知编译器T::const_iterator是个类型。
4 - hello2020-04-02上学的时候看了一遍c++ primer,学到的全是c++98。工作遇到c++14或17新语法时,一头雾水。只能在工作中慢慢摸索,一直想看到一篇只讲新老版本c++每一个区别的文章。发现这篇对我来说真的适合3
- 晚风·和煦2020-02-16转发引用就是万能引用吗😁😂
作者回复: 对。C++11刚出来时,对T&&没有专门的术语,是Scott Meyers发明了“万能引用”这一术语来描述。后来标准委员会认识到我们需要一个专门术语,在C++17把这个概念用“转发引用”来描述了。为什么叫这个名字,可以参见: https://isocpp.org/files/papers/N4164.pdf
2 - 阿白2021-10-29老师我在练习结构化绑定的时候发现一个问题,下面这个例子 int p = 1; char l = 1; int m = 1; std::tuple<float &, char &&, int> tpl(p, std::move(l), m); const auto&[a, b, c] = tpl; 最终a,b,c类型的推断结果为float & a, char && b, const int& c 引用类型的cosnt限定被忽略了,我去查资料查到 Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef or of a template type argument, in which case the cv-qualifiers are ignored. 我在测试的时候感觉很混乱,到底什么情况下会出现ill-formed的Cv-qualified references。展开
作者回复: 你举的这个例子相当复杂。常见情况下,一般不会用引用绑定去绑定到一个带引用的 tuple 上,所以我也不讨论这样的特殊情况了。 对于这个例子,编译器的第一步动作是: const auto& e = tpl; 我们随即得到 e 的类型是 const std::tuple<float &, char &&, int>&。 a、b、c 本质上就是 get<0>(e)、get<1>(e)、get<2>(e) 的语法糖了。对于 tuple 里的引用成员,get 的结果就是这个引用成员,所以没有 const:你对它修改时没有改变 tuple 本身,而是改变它指向的内容(可以把引用改成指针来想象一下;引用可以看作会自动解引用的指针)。对于 tuple 里的非引用成员,get 的结果实际是指向 tuple 的引用(即使你获得的结果不是引用)。这就好比 s.int_value 的类型是整数,但你修改了 s.int_value 会修改 s 一样。 下面的例子可以帮助你理解: int n = 0; std::tuple<int*, int> tup{&n, n}; auto& [p, m] = tup; m = 1; 到此为止,tup 里的整数也变成了 1。
共 3 条评论1 - Geek_b68b742020-01-17什么时候该用auto,什么时候该用decltype呢?隐隐知道怎么用,但不知道具体的规则是什么呢
作者回复: 就看你要不要保持值类别了。在应用的代码,一般写 auto 更安全。如果你是写通用的模板代码,那可能就需要写 decltype(auto) 了。但你得仔细考虑一下对象的生命期问题,确保不会返回一个过期的引用。
1 - EncodedStar2019-12-26auto 用了不少,真的好用,其他的都没有真正用到。工作中的项目都不支持11,只能自己学习的时候使用了。
作者回复: 唉……想办法看看能不能把工具链先升级上去,并测试有没有问题吧。
共 4 条评论1 - A君2022-05-10请问同样是在函数中返回一个对象,一个是定义函数的返回值类型为引用:A& func(...) { ... A a; ... return a; },另一个则是通过类型推导为函数返回值绑定类型:auto& a = func(...) {... return a; }。这两者是等价的吗?
作者回复: 不等价,两个都是大错特错。 前一个是返回本地变量的引用,未定义行为,但应该能通过编译。 后一个是拿左值引用绑定到一个临时对象上,编译都过不去。 不过,后者如果把“auto&”改成“const auto&”或“auto&&”的话,代码就合法了——生命周期延长规则就生效了。
共 3 条评论 - 常振华2021-09-30说实话,我不觉得这些语法更易用,虽然增加了灵活性,但是越灵活的东西就越复杂。
作者回复: 让新手上手更容易,让代码更简洁。 确实不是每个人都喜欢每个 C++ 的新特性。一门有着众多用户的庞大语言,要改进是很不容易的。
- 怪兽2021-06-19我孤陋寡闻了,C++的语法分析竟然会在隐式函数转换上失败,我试了一下,如果这样写就可以: ifstream ifs(utf8_to_wstring(filename).operator wchar_t*()); 另外,我在使用map容器的时候,总是first、second,我觉得应用结构化绑定更直观些,那么是否依旧可以使用&(引用)符号呢?例如auto&,这样就不会发生拷贝了吧: std::map<int, string> students; for (const auto& [id, name] : students) { cout << id << name << endl; }展开
作者回复: 对,对于遍历关联容器,你写的是标准的惯用法。
- Geek_dddde92021-06-14主要还是用auto 统一初始化 默认初始化 范围for,这种确实很容易简化代码
- chang2021-05-13老师好。有点没有搞懂,StrVec是一个自定义的存储std::string元素类型的动态数组: class StrVec { public: explicit StrVec(initializer_list<std::string> ls); }; 当上面这个构造函数指定为explicit时,StrVec sv={"hello", "world"}; 这样定义会报错;当不指定explicit时,这样定义ok,不知为啥?展开
作者回复: 你定义的形式是拷贝初始化。去掉sv后面的等号即可。
共 2 条评论 - 2012015112021-04-14class Complex {public: Complex() {} Complex(float re) : re_(re) {} Complex(float re, float im) : re_(re) , im_(im) {}private: float re_{0}; float im_{0};}; 功能安全26262要求,构造函数不要同时使用默认初始化和列表初始化,想请教下,从c++角度看这个要求有合理性吗?
作者回复: 哪儿来的要求?我看不出这样的要求能有什么好处。 难道只允许一个构造函数,或者不允许默认成员初始化么?只要有超过一个构造函数,又有默认成员初始化,我感觉基本上就会违反这一条了。
- xl0002020-04-02老师,decltype(auto) a = expr;这种写法,根auto a = expr;有什么区别呢?它能正确地推断出需要写auto& a = expr;的情况?
作者回复: 区别不是写在正文里了么? 对,在某些情况下它就相当于 auto& a = exp;。
共 2 条评论 - JDY2020-02-27这个我觉得我很有发言权,尤其是对auto, 刚开始学的时候用vscode,每次都给我识别不了,然后居然果断放弃了auto!现在看来真是浪费啊,老师能不能推荐一个很好的支持c++11的编译器呢?
作者回复: 你是说 IDE,而不是编译器吧…… 如果 Windows 上的话,就用 Visual Studio 2019 Community Edition 好了。 Mac 上的话,应该 Xcode 可以。 Linux 上不知道有很好用又免费的。如果只要求免费,试试 Vim 加 clang_complete(事实上,这是我的日常配置,但如果不是日常玩 Vim 的,我不推荐这个方案)。如果接受付费,可以用 CLion。
- 晚风·和煦2020-02-16假设由于某种原因,我们不能使用缺省参数来简化构造函数,我们可以用什么方式来优化上面这个代码呢? "缺省参数简化构造函数"不太懂,就是函数的默认参数吗?好像不算简化吧🤔
作者回复: 我的意思是,这个类我们本来也可以用下面的方式: class Complex { … Complex(float re = 0, float im = 0) : re_(re) , im_(im) {} }; 假如我们不用这种方式的话,该怎么办?
- tr2020-02-05老师,不明白为什么 float x ={123456789LL};这种算做类型收窄,会编译报错?
作者回复: 123456789 有 27 位有效数字(二进制),而 32 位的 float 类型的有效数字数是 24。你说损失了不? 详见 https://zh.wikipedia.org/zh-cn/%E5%96%AE%E7%B2%BE%E5%BA%A6%E6%B5%AE%E9%BB%9E%E6%95%B8
- Qi2019-12-23还在用Ti很旧的arm板子做开发,工具链都不支持C++11了,还是坚持看到现在了。。。
作者回复: 坚持向前看啊😄 看看工具链有没有升级版本可以支持更新的标准?