C++ 新版本特性 - C++14

参考资料

特性概览

c++14

变量模板

在C++14中:

  • lambda表达式参数可以为auto类型,类似于函数模板;
  • 可以对捕获列表的捕获变量“赋值”。
#include <iostream>
using namespace std;

template <class T>
constexpr T pi = T(3.1415926535897932385L); // 变量模板

template <class T>
T circular_area(T r)
{                       // 函数模板
  return pi<T> * r * r; // pi<T> 是变量模板实例化
}

int main()
{
  cout << circular_area(2) << endl;   // 12
  cout << circular_area(2.0) << endl; // 12.5664
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

别名模板

#include <iostream>
template <typename T, typename U>
struct A
{
  T t;
  U u;
};

template <typename T>
using B = A<T, int>;

int main()
{
  B<double> b;
  b.t = 10.5;
  b.u = 20.8;
  std::cout << b.t << ", " << b.u << std::endl; //10.5, 20
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

泛型lambda和lambda初始化捕获

#include <cmath>
#include <iostream>
using namespace std;

int main()
{
  int a = 2;

  // 捕获列表的捕获变量“赋值”
  [a = sin(a)]()
  {
    std::cout << a << std::endl;      // 0.909297
    std::cout << cos(a) << std::endl; // 0.6143
  }();

  std::cout << a << std::endl;      // 2
  std::cout << cos(a) << std::endl; // -0.416147

  // lambda表达式参数可以为auto类型
  auto f = [](auto a)
  { return a; };
  std::cout << f(1) << std::endl;   // 1
  std::cout << f(1.1) << std::endl; // 1.1
  return 0;
}
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

放松对constexpr函数的限制

C++11中的常量表达式函数:

  • 函数体只有单一的return返回语句;
  • 函数必须有返回值(不能是void函数)
  • 在使用前必须有定义
  • return返回语句表达式中不能使用非常量表达式的函数,全局数据,且必须是一个常量表达式
#include <cmath>
#include <iostream>
using namespace std;

constexpr int factorial(int n) { // 在C++11或者C++14中均可以编译通过
  return n <= 1 ? 1 : (n * factorial(n - 1));
}

constexpr int factorial_c14(int n) { // 只有在C++14中可以编译通过
  int ret = 1;
  for (int i = 1; i <= n; ++i) {
    ret *= i;
  }
  return ret;
}

int main()
{
  
  std::cout << factorial(5) << std::endl;   // 120
  std::cout << factorial_c14(5) << std::endl; // 在c++11下,会报error: body of ‘constexpr’ function ‘constexpr int factorial_c14(int)’ not a return-statement
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

deprecated标记

C++14中增加了[[deprecated]]标记,可以修饰类、函数、变量等,当程序中使用了被其修饰的模块时,编译器会产生告警,提示用户该标记标记的内容将来可能会被废弃,尽量不要使用。

#include <cmath>
#include <iostream>
using namespace std;

void [[deprecated]] fun(){};

int main()
{
  /*
  fun.cpp:6:6: warning: attribute ignored [-Wattributes]
    6 | void [[deprecated]] fun(){};
      |      ^
  fun.cpp:6:6: note: an attribute that appertains to a type-specifier is ignored
  */
  fun();
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

二进制字面量和数位分隔符

#include <cmath>
#include <iostream>
using namespace std;

int main()
{
  int a = 0b0001'1111'1010;
  double b = 3.14'1592'6535'8979;
  std::cout << "a:" << a << std::endl; // 506
  std::cout << "b:" << b << std::endl; // 3.14159

  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

库相关

  • std::make_unique
    • C++11中没有std::make_unique,在C++14中实现了这个方法
  • std::shared_timed_mutex和std::shared_lock
    • C++14中通过std::shared_timed_mutex和std::shared_lock实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作不能和读操作同时进行。
  • std::integer_sequence
    • 表示一个编译时的整数序列。在用作函数模板的实参时,能推导参数包Ints并将它用于包展开。
  • std::exchange
    • 使用new_value 替换 obj 的值,并返回 obj 的旧值。(右边替换左边,返回左边的初值);T 必须满足可移动构造 (MoveConstructible) 的要求。而且必须能移动赋值 U 类型对象给 T 类型对象。 <<< @/md/c++/features/c14/src/std_exchange.cpp
  • std::quoted
    • 用于给字符串添加双引号
#include <iostream>
#include <mutex>
#include <memory>
#include <thread>
#include <shared_mutex>
#include <array>
#include <tuple>
#include <utility>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <string>
#include <iterator>

struct A
{
};

void make_unique_fun()
{
	std::cout << "make_unique_fun" << std::endl;

	std::unique_ptr<A> ptr = std::make_unique<A>();
}

class ThreadSafe
{
public:
	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;
	}

private:
	mutable std::shared_timed_mutex _mutex;
	int _value;
};

void threadSafe_fun()
{
	std::cout << "threadSafe_fun" << std::endl;

	auto thread = new ThreadSafe();
	thread->increase();
	thread->increase();
	std::cout << "thread->get:" << thread->get() << std::endl;
}

template <typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq)
{
	std::cout << "The sequence of size " << int_seq.size() << ": ";
	((std::cout << ints << ' '), ...);
	std::cout << '\n';
}

// 转换数组为 tuple
template <typename Array, std::size_t... I>
auto a2t_impl(const Array &a, std::index_sequence<I...>)
{
	return std::make_tuple(a[I]...);
}

template <typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N> &a)
{
	return a2t_impl(a, Indices{});
}

// 漂亮地打印 tuple

template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch, Tr> &os, const Tuple &t, std::index_sequence<Is...>)
{
	((os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}

template <class Ch, class Tr, class... Args>
auto &operator<<(std::basic_ostream<Ch, Tr> &os, const std::tuple<Args...> &t)
{
	os << "(";
	print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
	return os << ")";
}

void integer_sequence_fun()
{
	std::cout << "print_sequence_fun" << std::endl;

	print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
	print_sequence(std::make_integer_sequence<int, 20>{});
	print_sequence(std::make_index_sequence<10>{});
	print_sequence(std::index_sequence_for<float, std::iostream, char>{});

	std::array<int, 4> array = {1, 2, 3, 4};

	// 转换 array 为 tuple
	auto tuple = a2t(array);
	static_assert(std::is_same<decltype(tuple), std::tuple<int, int, int, int>>::value, "");

	// 打印到 cout
	std::cout << tuple << '\n';
}

void exchange_fun()
{
	std::cout << "exchange_fun" << std::endl;

	std::vector<int> vec1{1, 2};
	std::vector<int> vec2{3, 4};

	std::cout << "exchange before: " << std::endl;
	std::cout << "vec1: " << std::endl;
	std::copy(vec1.begin(), vec1.end(), std::ostream_iterator<int>{std::cout, " "});
	std::cout << std::endl;
	std::cout << "vec2: " << std::endl;
	std::copy(vec2.begin(), vec2.end(), std::ostream_iterator<int>{std::cout, " "});

	exchange(vec1, vec2);

	std::cout << std::endl;
	std::cout << "exchange after: " << std::endl;
	std::cout << "vec1: " << std::endl;
	copy(vec1.begin(), vec1.end(), std::ostream_iterator<int>{std::cout, " "});
	std::cout << std::endl;
	std::cout << "vec2: " << std::endl;
	copy(vec2.begin(), vec2.end(), std::ostream_iterator<int>{std::cout, " "});
	std::cout << std::endl;
}

void quoted_fun()
{
	std::cout << "quoted_fun" << std::endl;

	std::string str{"hello world"};
	std::cout << "origin Str:" << str << std::endl;				 // hello world
	std::cout << "quoted Str:" << std::quoted(str) << std::endl; // "hello world"
}

int main()
{
	make_unique_fun();
	threadSafe_fun();
	integer_sequence_fun();
	exchange_fun();
	quoted_fun();
	return 0;
}
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

执行结果:

[root@iZuf61kbf845xt6tz10abgZ c14]# ./std_lib
make_unique_fun
threadSafe_fun
thread->get:2
print_sequence_fun
The sequence of size 7: 9 2 5 1 9 1 6
The sequence of size 20: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
The sequence of size 10: 0 1 2 3 4 5 6 7 8 9
The sequence of size 3: 0 1 2
(1, 2, 3, 4)
exchange_fun
exchange before:
vec1:
1 2
vec2:
3 4
exchange after:
vec1:
3 4
vec2:
3 4 
quoted_fun
origin Str:hello world
quoted Str:"hello world"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24