Effective Modern C++
本文是Effective Modern C++学习笔记,以细碎的知识点记录的形式来记录学习过程。
简介:
1、Effective Modern C++目标:学习怎样高效地使用新机能
2、移动语义(C++11): 右值表明这个对象适合移动操作,而左值一般不适合。右值对应于从函数返回的临时对象,而左值对应于你可以引用的(can refer to)对象,或者通过名字,或者通过指针或左值引用。对于判断一个表达式是否是左值的一个有用的启发就是,看看能否取得它的地址。
如果能取地址,那么通常就是左值。如果不能,则通常是右值
3、当使用另一个同类型的对象来初始化一个对象时,新的对象被称为是用来初始化的对象的一个副本(copy),尽管这个副本是通过移动构造函数创建的。
右值副本通常由移动构造产生,左值副本通常由拷贝构造产生。
4、在函数调用中,调用地传入的表达式称为函数的实参。实参被用来初始化函数的形参。实参和形参的区别非常重要,因为形参是左值,而用来初始化形参的实参可能是左值或者右值。
5、通过lambda表达式创建的函数对象称为闭包
6、申明和定义:
- 声明(declarations)引入名字和类型,并不给出比如存放在哪或者怎样实现等的细节
- 定义(definitions)提供存储位置或者实现细节
7、std::auto_ptr在C++11中被废弃,但std::unique_ptr可以做同样的工作,而且做的更好。
第1章 类型推导
条款一:理解模板类型推导 : Understand template type deduction
1、C++98就有了类型推导,C++11起加入了auto
和decltype
,c++14对其可能使用的范围进行了拓展。
2、只有在传值给形参时才会忽略const(和volatile)
3、函数不能声明形参为真正的数组,但是可以接受指向数组的引用;在直接传递数组的时候,数组会退化成指针形式。函数类型也会退化为一个函数指针。
3、auto依赖于模板类型推导
条款二:理解auto类型推导 : Understand auto type deduction
1、auto的特殊情况:对于花括号的处理是auto类型推导和模板类型推导唯一不同的地方。
auto x3 = { 27 }; //类型是std::initializer_list<int>,x3值是{ 27 } OK
auto x4{ 27 }; //同上 OK
auto x5 = { 1, 2, 3.0 }; //错误!无法推导std::initializer_list<T>中的T ,花括号里面包含的是不同类型的变量,编译器会拒绝这样的代码.
auto createInitList()
{
return { 1, 2, 3 }; //错误!不能推导{ 1, 2, 3 }的类型
}
2、在C++14中auto允许出现在函数返回值或者lambda函数形参中,但是它的工作机制是模板类型推导那一套方案,而不是auto类型推导。
总之就是不要使用auto 推导花括号类型,这个活他干不了!
条款三:理解decltype : Understand decltype
decltype: 给它一个名字或者表达式decltype就会返回这个名字或者表达式的类型。在C++11中,decltype最主要的用途就是用于声明函数模板,而这个函数返回类型依赖于形参类型。vector除外!
在函数形参列表后面使用一个->
符号指出函数的返回类型,尾置返回类型的好处是我们可以在函数返回类型中使用函数形参相关的信息。
C++14中可以使用decltype(auto):auto说明符表示这个类型将会被推导,decltype说明decltype的规则将会被用到这个推导过程中。
decltype(auto)的使用不仅仅局限于函数返回类型,当你想对初始化表达式使用decltype推导的规则,你也可以使用:
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; //auto类型推导 myWidget1的类型为Widget
decltype(auto) myWidget2 = cw; //decltype类型推导 myWidget2的类型是const Widget&
用小括号覆盖一个名字可以改变decltype对于名字产生的结果:在:int x = 0;
中,x是一个变量的名字,所以decltype(x)是int。但是如果用一个小括号包覆这个名字,比如这样(x) ,就会产生一个比名字更复杂的表达式。对于名字来说,x是一个左值,C++11定义了表达式(x)也是一个左值。因此decltype((x))是int&。
所以,当使用decltype(auto)的时候一定要加倍的小心,在表达式中看起来无足轻重的细节(比如一个小括号)将会影响到decltype(auto)的推导结果。
条款四:学会查看类型推导结果 : Know how to view deduced types
- IDE编辑器: 在IDE中的代码编辑器通常可以显示程序代码中变量,函数,参数的类型,你只需要简单的把鼠标移到它们的上面,这种方式适用于比较简单的推导。
- **编译器诊断:**先声明一个类模板但不定义,尝试实例化这个类模板就会引出一个错误消息,因为这里没有用来实例化的类模板定义。在编译器中编译的报错中可以看到类型。
- 运行时输出:
std::cout << typeid(x).name() << '\n'; //显示x和y的类型
std::cout << typeid(y).name() << '\n';
但是,std::type_info::name
的结果并不总是可信的,可以使用Boost:
boost::typeindex::type_id_with_cvr()获取一个类型实参
第2章 auto
条款五:优先考虑auto而非显式类型声明: Prefer auto to explicit type declarations
auto变量从初始化表达式中推导出类型,所以我们必须初始化。lambda表达式中的形参也可以使用auto(C++14起)。
vectoer .size()
可以使用auto
推导
显式的指定类型可能会导致不希望看到的类型转换,使用auto可以避免很难被意识到的类型不匹配的错误,这样无疑更具效率,且更容易书写。
在某些情况下如果你的专业判断告诉你使用显式类型声明比auto要更清晰更易维护,那你就不必再坚持使用auto。
条款六:auto推导若非己愿,使用显式类型初始化惯用法 :
Item 6 : Use the explicitly typed initializer idiom when auto deduces undesired types
慎重使用auto
当有可能出现std::vector<bool>
作为一个通则,不可见的代理类通常不适用于auto。
解决方案是强制使用一个不同的类型推导形式,这种方法我通常称之为显式类型初始器惯用法。显式类型初始器惯用法使用auto声明一个变量,然后对表达式强制类型转换(cast)得出你期望的推导结果。举个例子:
auto highPriority = static_cast<bool>(features(w)[5]);
列举几个学习网站:
- Effective Modern C++中文翻译版1
- Effective Modern C++中文翻译版2