const
和/或volatile
实参会被认为是non-const
的和non-volatile
的auto
类型推导auto
类型推导通常和模板类型推导相同,但是auto
类型推导假定花括号初始化代表std::initializer_list
,而模板类型推导不这样做auto
允许出现在函数返回值或者lambda函数形参中,但是它的工作机制是模板类型推导那一套方案,而不是auto
类型推导decltype
decltype
总是不加修改的产生变量或者表达式的类型。T
类型的不是单纯的变量名的左值表达式,decltype
总是产出T
的引用即T&
。decltype(auto)
,就像auto
一样,推导出类型,但是它使用decltype
的规则进行推导。auto
而非显式类型声明auto
变量必须初始化,通常它可以避免一些移植性和效率性的问题,也使得重构更方便,还能让你少打几个字。auto
类型的变量可能会踩到一些陷阱。auto
推导若非己愿,使用显式类型初始化惯用法auto
从表达式中推导出“错误的”类型auto
推导出你想要的结果()
和{}
创建对象std::initializer_list
参数匹配,即便其他构造函数看起来是更好的选择std::vector
来说使用花括号初始化和小括号初始化会造成巨大的不同nullptr
而非0
和NULL
nullptr
而非0
和NULL
typedef
不支持模板化,但是别名声明支持(using ...)。::type
”后缀,而且在模板中使用typedef
还需要在前面加上typename
enum
而非未限域enum
enum
即非限域enum
。enum
的枚举名仅在enum
内可见。要转换为其它类型只能使用cast。enum
都支持底层类型说明语法,限域enum
底层类型默认是int
。非限域enum
没有默认底层类型。enum
总是可以前置声明。非限域enum
仅当指定它们的底层类型时才能前置。private
但不定义,使用deleted函数更好override
声明重写函数override
this
)const_iterator
而非iterator
const_iterator
而非iterator
begin
,end
,rbegin
等,而非同名成员函数noexcept
noexcept
是函数接口的一部分,这意味着调用者可能会依赖它noexcept
函数较之于non-noexcept
函数更容易优化noexcept
对于移动语义,swap
,内存释放函数和析构函数非常有用noexcept
constexpr
constexpr
对象是const
,它被在编译期可知的值初始化constexpr
函数可以产出编译期可知的结果constexpr
对象和函数可以使用的范围比non-constexpr
对象和函数要大constexpr
是对象和函数接口的一部分const
成员函数线程安全const
成员函数线程安全,除非你确定它们永远不会在并发上下文(concurrent context)中使用。std::atomic
变量可能比互斥量提供更好的性能,但是它只适合操作单个变量或内存位置。std::unique_ptr
std::unique_ptr
是轻量级、快速的、只可移动(move-only)的管理专有所有权语义资源的智能指针delete
实现,但是支持自定义删除器。有状态的删除器和函数指针会增加std::unique_ptr
对象的大小std::unique_ptr
转化为std::shared_ptr
非常简单std::shared_ptr
std::shared_ptr
为有共享所有权的任意资源提供一种自动垃圾回收的便捷方式。std::unique_ptr
,std::shared_ptr
对象通常大两倍,控制块会产生开销,需要原子性的引用计数修改操作。delete
,但是也支持自定义删除器。删除器的类型是什么对于std::shared_ptr
的类型没有影响。std::shared_ptr
。std::shard_ptr
可能悬空时使用std::weak_ptr
std::weak_ptr
替代可能会悬空的std::shared_ptr
。std::weak_ptr
的潜在使用场景包括:缓存、观察者列表、打破std::shared_ptr
环状结构。std::make_unique
和std::make_shared
而非new
new
相比,make
函数消除了代码重复,提高了异常安全性。对于std::make_shared
和std::allocate_shared
,生成的代码更小更快。make
函数的情况包括需要指定自定义删除器和希望用花括号初始化。std::shared_ptr
s,其他不建议使用make
函数的情况包括(1)有自定义内存管理的类;(2)特别关注内存的系统,非常大的对象,以及std::weak_ptr
s比对应的std::shared_ptr
s活得更久。std::unique_ptr
类型的pImpl
指针,需要在头文件的类里声明特殊的成员函数,但是在实现文件里面来实现他们。即使是编译器自动生成的代码可以工作,也要这么做。std::unique_ptr
,不适用于std::shared_ptr
。std::move
和std::forward
std::move
执行到右值的无条件的转换,但就自身而言,它不移动任何东西。std::forward
只有当它的参数被绑定到一个右值时,才将参数转换为右值。std::move
和std::forward
在运行期什么也不做。T&&
,并且T
需要被推导得知,或者如果一个对象被声明为auto&&
,这个形参或者对象就是一个通用引用。type&&
,或者如果类型推导没有发生,那么type&&
代表一个右值引用。std::move
,对通用引用使用std::forward
std::move
,在通用引用上使用std::forward
。std::move
或者std::forward
。const
左值,它们比拷贝构造函数而更匹配,而且会劫持派生类对于基类的拷贝和移动构造函数的调用。const
传递形参,按值传递形参,使用tag dispatch。std::enable_if
约束模板,允许组合通用引用和重载使用,但它也控制了编译器在哪种条件下才使用通用引用重载。auto
类型推导,typedef
与别名声明的创建和使用,decltype
。在大多数情况下,完美转发工作的很好。你基本不用考虑其他问题。但是当其不工作时——当看起来合理的代码无法编译,或者更糟的是,虽能编译但无法按照预期运行时——了解完美转发的缺陷就很重要了。同样重要的是如何解决它们。在大多数情况下,都很简单。
0
或者NULL
,仅有声明的整型static const
数据成员,模板和重载函数的名字,位域。this
指针),并且它会误导人产生lambda是独立的想法。std::bind
的方式来模拟初始化捕获。auto&&
形参使用decltype
以std::forward
它们auto&&
形参使用decltype
以std::forward
它们。std::bind
std::bind
相比,lambda更易读,更具表达力并且可能更高效。std::bind
可能对实现移动捕获或绑定带有模板化函数调用运算符的对象时会很有用。std::thread
API不能直接访问异步执行的结果,如果执行函数有异常抛出,代码会终止执行。std::async
进行基于任务的编程方式会解决大部分问题。std::launch::async
std::async
的默认启动策略是异步和同步执行兼有的。thread_local
s的不确定性,隐含了任务可能不会被执行的意思,会影响调用基于超时的wait
的程序逻辑。std::launch::async
。std::thread
在所有路径最后都不可结合thread
最终是不可结合的。join
会导致难以调试的表现异常问题。detach
会导致难以调试的未定义行为。std::thread
对象。std::async
启动的未延迟任务建立的那个——的最后一个future的析构函数会阻塞住,直到任务完成。void
的futuresstd::promise
和future的方案避开了这些问题,但是这个方法使用了堆内存存储共享状态,同时有只能使用一次通信的限制。std::atomic
,对于特殊内存使用volatile
std::atomic
用于在不使用互斥锁情况下,来使变量被多个线程访问的情况。是用来编写并发程序的一个工具。volatile
用在读取和写入不应被优化掉的内存上。是用来处理特殊内存的一个工具。囫囵吞枣式的看完了Modern C++,前面看的比较仔细,对应类型推导、智能指针、右值引用、通用引用以及一些好记的技巧等有了认识,对于Lambda、并发Api等有了点印象。
清楚了自己的不足,开发时不能停留在表明,每一步都有可能是坑。对自己不足的地方,后面会再系统的学习一次。并总结输出。特别是一些常用的知识。