文章目录
引言
打算花两天时间写三篇文章,把C++14,17,20已有的新特性说一说,为什么突然心血来潮想干这个事情呢,原因是在上课某个划水的课的时候玩手机看到了GCC最新的10.2.0版本在今年七月二十三号的时候已经发布了,在这个版本中已经支持了协程,并且在9月4日C++ 20的国际标准草案投票结束,而且获得了全票通过,这实在是让人心血澎湃啊!果断升级到最新版本,却猛然发现已经把C++11以上版本的特性忘的差不多了,恰好明天国庆,遂今天给自己放放假,就不动脑子了,边听歌边复习下这些特性吧。
C++14
总的的来说C++14并不是一个大更新,甚至可以说是一个很小的更新,因为没有什么让人眼前一亮的特性,基本是一些小玩意儿。
函数返回值推导
auto func_one(int i) {
return i;
}
template<typename T> auto func_one(T t){
// 模板中也可以这样玩
return t;
}
/*
auto func(bool flag) {
if (flag) return 1;
else return 2.3; // 在出现多个类型分支的时候没办法推导
}
auto func() {
return {1, 2, 3}; // 初始化列表也没办法被推导
}
*/
cout << func_one(2) << " " << func_one<double>(5.5) << endl;
输出:
2 5.5
lambda参数auto
auto func_two = [] (auto a) {
return a;};
cout << func_two("hello world!") << " " << func_two("1024") << endl;
输出:
hello world! 1024
decltype(auto)
C++11中有两种推断类型的方式。auto根据给出的表达式产生具有合适类型的变量。decltype可以计算给出的表达式的类型。但是,decltype和auto推断类型的方式是不同的。特别地,auto总是推断出非引用类型,就好像使用了std::remove_reference一样,而auto&&总是推断出引用类型。然而decltype可以根据表达式的值类别(value category)和表达式的性质推断出引用或非引用类型
这篇文章把这个问题描述的非常全面《C++14尝鲜:decltype 和 decltype(auto)》
Lambda捕获部分中使用表达式
这意味着我们可以在捕获子句(capture clause)中,也就是[ ]中增加并初始化新的变量,该变量不需要在lambda表达式所处的闭包域中存在
auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda = [value = std::move(ptr)] {
return *value;}
变量模板与别名模板
template<class T> // 变量模板 最典型的用法就是pi,在以前只能用一个没有参数的模板函数来返回
constexpr T pi = T(3.1415926535897932385L);
template<typename T, typename U>
struct str_three{
T t;
U u;
};
// C++11引入类型别名的原因是因为无法在typedef语句的基础上直接构建别名模板
// 别名模板可以使声明一个只带一个模板参数T的类模板,使其等价于模板参数T
// 为int类型的str_three模板
template<typename U>
using var_three = str_three<int, U>;
----------------------------------------
cout << pi<int> << " " << pi<double> << endl;
var_three<int> temp_three;
temp_three.t = 10;
temp_three.u = 20;
cout << temp_three.t << " " << temp_three.u << endl;
输出:
3 3.14159
10 20
下面这篇文章讲的非常详细:《C++14尝鲜:别名模板和变量模板》
更为宽松的constexper
/**
* C++11: 1. 函数的返回类型以及所有形参的类型都得是字面值类型
* 2. 函数体中只能由一个return语句
* C++14: 1. 可以使用局部变量和循环
* 2. 可以使用多个return
*/
constexpr int factorial_four(int n) {
// C++14 和 C++11均可
return n <= 1 ? 1 : (n * factorial_four(n - 1));
}
// 多返回值与局部变量
constexpr int factorial(int n) {
// C++11中不可,C++14中可以
if(n < 0) return 0;
int ret = 0;
// int items[20]; 这样定义是不对的
for (int i = 0; i < n; ++i) {
ret += i;
}
return ret;
}
输出:
10
120
[[deprecated]]标记
// 编译时被产生警告,用户提示开发者该标记修饰的内容将来可能会被丢弃,尽量不要使用
// 可以修饰类、变、函数等
struct [[deprecated]] var_five {
int item;
};
[[deprecated]] constexpr int func_five(){
return 200;
}
二进制字面量与整形字面量分隔符
int a = 0b0001'0011'1010;
double b = 3.14'1234'1234'1234;
// 这种字符串的语法糖别忘了C++11的原生字符串
integer_sequence 类
《integer_sequence 类》
《模板展开及integer_sequence妙用》
它的作用为:类模板 std::integer_sequence 表示一个编译时的整数序列。在用作函数模板的实参时,能推导参数包 Ints 并将它用于包展开。
但是这个东西我不太清楚有什么实际的用处,可能在元编程中有一些奇奇怪怪的用处吧。现在我能看到的一个用处就是把一个容器的每一项在编译期当做一个函数的参数,这个作用在C++17中可以被std::apply代替。
std::make_unique
std::unique_ptr<int> ptr = std::make_unique<int>(5);
cout << *ptr << endl;
输出:
5
这没什么说的,不清楚为什么C++11中没有。
std::shared_timed_mutex与std::shared_lock
不清楚为什么14有了shared_timed_mutex,shared_mutex17才有。
把这东西当做一个带超时的读写锁就可以了,至于shared_lock,读的时候我们当然不能使用unique_ptr啦。
下面的代码是从别人那里直接拿的,可以理解为一个简单的读写锁。
struct ThreadSafe {
mutable std::shared_timed_mutex mutex_;
int value_;
ThreadSafe() {
value_ = 0;
}
int get() const {
std::shared_lock<std::shared_timed_mutex> loc(mutex_);
return value_;
}
void increase() {
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
value_ += 1;
}
};
其中与超时有关的借口可以直接查看官网传送门
std::exchange
它与std::swap有什么区别呢?我们来看一个简单的样例:
std::vector<int> items;
std::exchange(items, {
1,0,2,4});
for (int x : items) {
cout << x << " ";
}
输出:
1 0 2 4
看起来好像与swap一样,我们来看看exchange的实现:
template <typename _Tp, typename _Up = _Tp>
inline _Tp
exchange(_Tp& __obj, _Up&& __new_val)
{
return std::__exchange(__obj, std::forward<_Up>(__new_val)); }
template <typename _Tp, typename _Up = _Tp>
inline _Tp
__exchange(_Tp& __obj, _Up&& __new_val)
{
_Tp __old_val = std::move(__obj);
__obj = std::forward<_Up>(__new_val);
return __old_val;
}
简单来说就是这样:
template<class T, class U = T>
constexpr T exchange(T& obj, U&& new_value) {
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
也就是把后面参数的值全部通过move转移到第一个参数上,不过第一个参数的值并不进行转移,源码让人想起了引用折叠和完美转发,忘记的朋友可以去复习复习。
标准自定义字面量
- “s”,用于创建各种std::basic_string类型。
- “h”、“min”、“s”、“ms”、“us”、“ns”,用于创建相应的std::chrono::duration时间间隔。
- “if”、“i”、"il"用于创建相应的 std::complex、 std::complex 和 std::complex 复数类型。
auto str = "hello world"s; // 自动推导为 std::string
auto dur = 60s; // 自动推导为 chrono::seconds
auto z = 1i; // 自动推导为 complex<double>
参考可能看起来有些乱,因为这些是看这些特性时的全部参考,包括后面要写的C++17,这里附上了参考,后面就不写啦
参考: