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

DANGER

C++20下面的代码未在环境上编译测试使用,仅用于理论知识补充,后期升级gcc版本再进行实测。

参考资料

特性概览

c++20_1

c++20_2

c++20_3

The Big Four

Concepts

C++中模板编程极大地简化了代码的编写,但同时也会带来一些使用上的疑惑,例如,判断两个数是否相等,C++17提供了concept机制,用来检查模板参数类型:

#include <concepts>
#include <iostream>

template <typename T>
concept Integeral = std::is_integral<T>::value;

template <Integeral T>
auto isEqual(T left, T right) {
  return left == right;
}

int main() {
  cout << std::boolalpha;

  double a = 2.334;
  double b = 2.335;

  std::cout << isEqual(2, 2) << std::endl;         // true
  std::cout << isEqual(b - a, 0.001) << std::endl; // false

  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Range library

ranges:代表一段元素,之前版本使用begin和end标识一段元素。

ranges优点:

  • 简化语法和操作;
  • 防止begin,end迭代器的不配对使用;
  • 使得类似管道|的串行操作成为可能。

相关概念:

  • View:延迟计算,只有读权限
  • Actions:即时处理,读或写
  • Algorithms:操作range
  • Views和Actions的串联操作
#include <concepts>
#include <iostream>

//简化操作:
void simple_action()
{
  std::cout << "simple_action" << std::endl;
  std::vector<int> vec{3, 1, 2, 5, 6, 4};

  // std::sort()
  std::sort(vec.begin(), vec.end());
  std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>{cout, " "}); // 1 2 3 4 5 6

  // ranges::sort()
  std::ranges::sort(vec);
  std::ranges::copy(vec, std::ostream_iterator<int>{cout, " "}); // 1 2 3 4 5 6
}

//串联视图:
void view_action()
{
  std::cout << "view_action" << std::endl;
  std::vector<int> vec{1, 2, 3, 4, 5, 6};

  auto even = [](int i){ return i % 2 == 0; };
  auto square = [](int i){ return i * i; };

  auto result = vec | std::views::filter(even) | std::views::transform(square);
  /* 此时 result 并没有被计算,只是定义了一系列视图,只有到下面的遍历操作时,result 触发,即所谓延迟计算 */
  for (auto i : result)
  {
    cout << i << " "; // 4 16 36
  }
}

//过滤和变换:
void filter_transfrom_action()
{
  std::cout << "filter_transfrom_action" << std::endl;

  std::vector vec{1, 2, 3, 4, 5};

  auto result = vec | std::views::transform([](int i){ return i * i; }) | std::views::take(3);

  cout << std::accumulate(result.begin(), result.end(), 0) << endl; // 14
}

int main()
{
  simple_action();
  view_action();
  filter_transfrom_action();

  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

Coroutines

所谓协程,是一个函数,能在保持状态的时候暂停或者继续。具有关键字:

  • co_await:挂起协程,等待其它计算完成
  • co_return:退出协程
  • co_yield:弹出一个值,挂起协程,等待下一次调用,类似于Python中的生成器

待补充内容

Modules

关键字:import,export

新建测试文件test_module.ixx和main.cpp,注意模块文件后缀名为.ixx,如果为.cpp文件可能需要去命令行编译,使用方式和include差不多,但是说使用模块比使用include效率要高很多:

// test_module.ixx
export module test_module;

namespace _module {
auto _print() { return "hello world"; }
export auto print() { return _print(); }
} // namespace _module

/*——————————————————————————————————————————*/

// main.cpp
import test_module;
#include <iostream>

int main() {
  std::cout << _module::print() << std::endl; // hello world
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

模块优势:

  • 没有头文件
  • 声明和实现仍然可以分离,但没有必要
  • 可以显式指定导出目标对象(函数,类)
  • 没有头文件重复包含风险
  • 模块名称可以相同
  • 模块只处理一次,编译更快(头文件每次引入都需要处理)
  • 预处理宏只在模块内有效
  • 模块引入顺序无关紧要

Core Language

特性测试宏

通过它可以判断编译器是否支持某个功能,例如:

// 语言特性
__has_cpp_attribute(fallthrough)
__cpp_binary_literals
__cpp_char8_t
__cpp_coroutines
// 标准库特性
__cpp_lib_concepts
__cpp_lib_ranges
__cpp_lib_scoped_lock
1
2
3
4
5
6
7
8
9

三路比较运算符<=>

(x <=> y) < 0 // 如果 x < y, 返回 true
(x <=> y) > 0 // 如果 x > y, 返回 true
(x <=> y) == 0 // 如果 x == y, 返回 true
1
2
3

可以使用以下代码代替所有的比较运算符:

auto X::operator<=>(const X&) = default;
1

范围for循环初始化

class Data {
public:
  Data(){}
  Data getData() { return Data(); }
private:
  std::vector<int> values;
};

int main() {
  for (auto data = getData(); auto &value : data.values) {
    cout << value << " ";
  }

  return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

待补充