C++14特性汇总

有关auto

函数值可以返回auto

auto关键字可以用于函数返回值推导和lambda表达式参数列表的类型推导

1
2
template<typename T>
auto foo(T t) { return t; }

甚至这样也可以:有一个退出路径返回了具体类型:

1
2
3
4
5
auto fabonaci(int n) {
if (1 == n || 2 == n)
return 1;
return fabonaci(n - 1) + fabonaci(n - 2);
}

可以实现泛型lambda

1
2
3
4
5
6
struct unnamed_lambda
{
template<typename T, typename U>
auto operator()(T x, U y) const {return x + y;}
};
auto lambda = unnamed_lambda();

现在泛型lambda十分简单:

1
auto lambda = [](auto x, auto y) {return x + y;};

Generalized lambda capture

1
2
3
4
5
6
unique_ptr<int>b = std::make_unique<int>(5);
auto fun = [x = std::move(b)](int t = 5)
{
cout<<*x<<endl;
};
fun();

更宽松的constexpr

constexpr 最常用的用法就是修饰函数
如下所示,用 constexpr 来修饰一个函数。C++11 中的 constexpr 指定的函数返回值和参数必须要保证是字面值,而且必须有且只有一行 return 代码,这给函数的设计者带来了更多的限制,通常只能通过 return 三目运算符 + 递归来计算返回值,如下例所示。

1
2
3
constexpr int factorial(int n) { // C++14 和 C++11均可
return n <= 1 ? 1 : (n * factorial(n - 1));
}

C++14 对于 constexpr 的改进
而 C++14 中只要保证返回值和参数是字面值就行了,函数体中可以加入更多的语句,方便了更灵活的计算和复杂功能的实现。

1
2
3
4
5
6
7
constexpr int factorial(int n) { // C++11中不可,C++14中可以
int ret = 0;
for (int i = 0; i < n; ++i) {
ret += i;
}
return ret;
}

变量模板

1
2
3
4
template<typename T>
constexpr T pi() {
return T(3.1415926535897932385);
}

std::make_unqiue

为了配对make_shaderd

std::exchange

  • std::exchange()使用新值替换旧值,并返回旧值,新值不变,仅是资源的转移,而不是的交换。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //<utility>
    template <typename _Tp, typename _Up = _Tp>
    constexpr inline _Tp exchange(_Tp& __obj, _Up&& __new_val)
    {
    return std::__exchange(__obj, std::forward<_Up>(__new_val));
    }

    //<move.h>
    template <typename _Tp, typename _Up = _Tp>
    constexpr inline _Tp __exchange(_Tp& __obj, _Up&& __new_val)
    {
    _Tp __old_val = std::move(__obj);
    __obj = std::forward<_Up>(__new_val);
    return __old_val;
    }
  • std::swap()才是真正的交换,两个元素的值进行了互换。
1
2
3
4
5
6
7
8
//<utility> -> <move.h>
template<typename _Tp>
constexpr inline void swap(_Tp& __a, _Tp& __b)
{
_Tp __tmp = std::move(__a);
__a = std::move(__b);
__b = std::move(__tmp);
}

读写锁

什么是互斥锁和读写锁:

互斥锁,是只同一时刻只能有一个线程获得锁,其余尝试加锁的线程都处于阻塞状态。而相比于互斥锁的两种状态(锁住和未加锁),读写锁可以有三种状态,即读模式加锁、写模式加锁和未加锁。只有一个线程可以处于写模式加锁状态,但是可以有多个线程同时处于读模式加锁状态。

锁状态 加读锁 加写锁
写模式加锁状态 失败 失败
读模式加锁状态 成功 失败
未加锁状态 成功 成功
  • 读写锁又称为共享-独占锁。写锁是独占的,其余加锁行为都会阻塞;读锁是共享的,多个线程可同时获得读锁,但加写锁会阻塞。
  • 现代C++提供了std::shared_mutex(C++17)std::shared_timed_mutex(C++14)两种共享互斥量,又提供了std::shared_lock(C++14)std::unique_lock(C++11)共同管理这类互斥量来实现读写锁。
  • std::shared_lock只是对共享互斥量的一种包装器,提供了更加安全方便的调用操作。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <shared_mutex>
#include <thread>
#include <chrono>

//std::shared_lock<std::shared_mutex> 超过作用域即结束
void test_shared_lock() {
int num = 0;
std::shared_mutex mtx;

auto reading = [&]() {
for (int i = 0; i < 3; ++i) {
std::shared_lock<std::shared_mutex> lck(mtx);
std::cout << std::this_thread::get_id() << " reading: " << num << std::endl;
}
};

auto writing = [&]() {
for (int i = 0; i < 3; ++i) {
std::unique_lock<std::shared_mutex> lck(mtx);
std::cout << std::this_thread::get_id() << " writing: " << ++num << std::endl;
}
};

std::thread r1(reading);
std::thread w1(writing);
std::thread r2(reading);
std::thread w2(writing);

r1.join();
w1.join();
r2.join();
w2.join();
}

//std::shared_lock<std::shared_timed_mutex> 设置一段时间结束后解锁
void test_shared_lock_for() {
int num = 0;
std::shared_timed_mutex mtx;

auto reading = [&]() {
for (int i = 0; i < 3; ++i) {
std::shared_lock<std::shared_timed_mutex> lck(mtx, std::chrono::milliseconds(10));
std::cout << std::this_thread::get_id() << " reading: " << num << std::endl;
}
};

auto writing = [&]() {
for (int i = 0; i < 3; ++i) {
std::unique_lock<std::shared_timed_mutex> lck(mtx, std::chrono::milliseconds(10));
std::cout << std::this_thread::get_id() << " writing: " << ++num << std::endl;
}
};

std::thread r1(reading);
std::thread w1(writing);
std::thread r2(reading);
std::thread w2(writing);

r1.join();
w1.join();
r2.join();
w2.join();
}

//std::shared_lock<std::shared_timed_mutex>到达指定之间点后解锁
void test_shared_lock_until() {
int num = 0;
std::shared_time_mutex mtx;

auto reading = [&]() {
for (int i = 0; i < 3; ++i) {
std::shared_lock<std::shared_timed_mutex> lck(mtx, std::chrono::time_point<std::chrono::high_resolution_clock>(std::chrono::milliseconds(10)));
std::cout << std::this_thread::get_id() << " reading: " << num << std::endl;
}
};

auto writing = [&]() {
for (int i = 0; i < 3; ++i) {
std::unique_lock<std::shared_timed_mutex> lck(mtx, std::chrono::time_point<std::chrono::high_resolution_clock>(std::chrono::milliseconds(10)));
std::cout << std::this_thread::get_id() << " writing: " << ++num << std::endl;
}
};

std::thread r1(reading);
std::thread w1(writing);
std::thread r2(reading);
std::thread w2(writing);

r1.join();
w1.join();
r2.join();
w2.join();
}

int main() {
test_shared_lock();
//test_shared_lock_for();
//test_shared_lock_until();
return 0;
}

std::integer_sequence

https://zhxilin.github.io/post/tech_stack/1_programming_language/modern_cpp/cpp14/more_cpp14/#stdinteger_sequence

常见的用法是遍历tuple,更多的用处是配合C++17的折叠表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename Tuple, typename Func, size_t ... N>
void func_call_tuple(const Tuple& t, Func&& func, std::index_sequence<N...>) {
static_cast<void>(std::initializer_list<int>{(func(std::get<N>(t)), 0)...});
}

template <typename ... Args, typename Func>
void travel_tuple(const std::tuple<Args...>& t, Func&& func) {
func_call_tuple(t, std::forward<Func>(func), std::make_index_sequence<sizeof...(Args)>{});
}

int main() {
auto t = std::make_tuple(1, 4.56, "happen lee");
travel_tuple(t, [](auto&& item) {
std::cout << item << ",";
});
}

二进制字面量

1
2
3
4
5
6
7
// the answer to life, the universe, etc. in...
auto a1 = 42; // ... decimal
auto a2 = 0x2A; // ... hexadecimal
auto a3 = 0b101010; // ... binary

int a = 0b0001'1001'0001;
double b = 3.141'5926'5358'9793'2385;

[[deprecated]标记

让编译产生警告

1
2
3
4
5
6
7
#include <iostream>

void [[deprecated]] f() { std::cout << "f()" << std::endl; }

int main() {
f();
}

std::quoted

C++14引入std::quoted用于给字符串添加双引号

1
std::cout<<std::quoted(mystring);

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!