C++11/14/17新特性拾遗
⓫变长参数模板 (Variadic Template)
...
1 | template<typename... T> |
参数解包有以下几种方式
1 | void print() { cout << '\n'; } |
1 | template<typename T, typename... Ts> |
1 | template<typename T, typename... Ts> |
⓫模板元编程 (TMP; Template Meta Programming)
基础
元程序即
Enum Hack
使用枚举常量代替整型常量
1 | template<int N> struct fib { |
特性萃取技术
需包含如下头文件
1 |
在编译期确定某种类型是否具有某种特性
struct
属于 class
, is_class
用于检测是否为 struct
或 class
。
函数指针不被
is_function
检测 。
1 | using INT = int; |
确定数组之维度
若出现错误
则 , value
为 0 。
1 | cout << std::rank<int[10]>::value << '\n' << // 1 |
SFINAE
SFINAE(Substitution Failure is not an Error)
匹配失败不是错误
1 | struct Test { using Foo = int; }; |
⓫其他模板强化
模板别名
1 | template<typename T> |
模板默认参数
1 | template<typename T = int> ...; |
外部模板
1 | extern template class A<TYPE>; // 不在该当前编译文件中实例化 |
⓫右值引用
右值引用使临时值的生命周期延长至变量存货周期
左值
能被取地址的都是左值: ; 因此
右值引用变量, 字符串字面量等都是左值、 。 右值
(字符串字面量以外的): 。
将亡值 (XValue; Expiring Value)
与右值引用相关
引用类型及其可以引用的值类型如下表
引用类型 | 非常左值 | 常左值 | 非常右值 | 常右值 | 注记 |
---|---|---|---|---|---|
非常量左值引用 | ✓ | 无 | |||
常量左值引用 | ✓ | ✓ | ✓ | ✓ | 全能类型 |
非常量右值引用 | ✓ | 用于移动语义 | |||
常量右值引用 | ✓ | ✓ | 暂无用途 |
1 | const int && p = 5; |
⓫auto
和 decltype
的类型推断
auto
, auto&&
则总是引用类型; decltype(e)
, decltype((e))
。
auto
推断丢失引用信息 , decltype
推断保留引用信息 。
1 | int x = 0; |
尾返回类型 (Trailing Return Type) 推导
1 | template<typename T, typename U> |
⓮普通函数具备返回值推导能力
1 | template<typename T, typename U> |
⓮泛型 lambda 表达式
使用
auto
而非 template
。
1 | auto f = [](auto x) { ... }; |
⓮decltype(auto)
主要用于对转发函数或封装的返回类型进行推导
1 | auto f = []() -> int&& {}; |
此处x3
1 | decltype(auto) x3 = f(); // x3: int&& |
⓫移动语义
1 | class Moveable { |
使用移动语义的emplace_back()
1 | vector<complex<double>> v; |
引用折叠/坍缩 (Reference Collapsing)
又称左值引用传染
T&
+&
=T&
T&
+&&
=T&
T&&
+&
=T&
T&&
+&&
=T&&
完美转发 (Perfect Forwarding)
在函数模板中
1 | void check(int &) { cout << "lvalue\n"; } |
⓫继承构造函数
1 | class Derived: public Base { |
⓫线程 (Thread)
Linux
下需要向编译器指明 -pthread
。
基础
join()
等待线程结束: ; detach()
分离执行: 互不影响, 。
Once a thread detached, we cannot force it to join with the main thread again.
1 | t.detach(); |
可使用joinable()
1 | if(t.joinable()) t.join(); |
新建线程
1 | std::thread t((Functor())); |
传递引用参数时&
标记
1 | std::thread t(&thread_function, std::ref(s)); |
Copying a thread won't compile, but we can transfer the ownership by moving.
1 | std::thread t2 = std::move(t1); |
通过std::this_thread::get_id()
通过std::thread::hardware_concurrency()
互斥量与临界区
RAII(Resource Acquisition is Initialization)
资源获取即初始化std::lock_guard
1 | void critical_section(int value) { |
std::lock_guard
lock()
unlock()
std::unique_lock
If we have an exception after lock() & before unlock(), then we'll have a problem.
1 | // 不推荐 |
借此可实现经典的
下例
wait()
中传入 lck
的原因是 条件变量需要先解锁互斥量 , 然后阻塞 , 唤醒后再锁上 , 。
1 | mutex mtx; |
1 | void producer(...) { |
1 | void consumer() { |
期物和诺物 (std::future
&std::promise
)
下例中f.get()
f.wait()
1 | void get(const std::future<int> & f) { int res = f.get(); ... } |
future
packaged_task
packaged_task
用于封装任何可调用的目标 。
1 | std::packaged_task<int()> task([](){ return ...; }); |
future
async()
async()
的第一个参数 , std::launch::async
指示异步求值 , std::launch::deferred
指示惰性求值 默认 , std::launch::async|std::launch::deferred
即取决于实现 , 。
1 | std::future<int> f = std::async(std::launch::async, [](){ |
⓫自定义字面值
1 | long operator"" _kb(long v) { return v * 1024; } |
⓱变量声明强化
if
/switch
1 | if(const auto it = find(v.begin(), v.end(), x); it != v.end()) { ... } |
⓱结构化绑定
下例中
, f()
返回一个 std::tuple
。
1 | auto [x, y, z] = f(); |
⓫以往使用std::tie
1 | tie(x, y, z) = f(); |
⓱非类型模板参数推导
1 | template<auto value> void f() { ... } |
⓱折叠表达式 (Fold Expression)
1 | template<typename... T> |
左折叠(...+t)
1+(2+(3+(4+5)))
右折叠(t+...)
(((1+2)+3)+4)+5
空包
参数包参数数目为sum();
解决空包出错可以将sum()
1 | template<typename... T> |
其他
使用0B
1 | int a = 0B101010101010; |
使用'
对数字分组
1 | int b = 564'190'000; |
新标准弃用的特性
字符串字面量需要const char*
指向
1 | char* str = "..."; // Deprecated! |
register
auto_ptr
unique_ptr