C++20

C++20，是继C++17之后的C++编程语言的ISO/IEC标准修订版的名称。2020年2月，该标准在布拉格的会议上由WG21进行了技术定稿。同年9月4日草案获得批准后，C++20同年12月正式发布。相比 C++17，C++20引入了新的语言特性，如概念、模块、操作符“”、协程、指定初始化、新标准属性等。C++20库标准还加入了范围、特性测试宏和位操作等。

**特性改动**

**新增语言特性**

**功能特性测试**
自C++11之后标准引入了大量的C++语言、库特性，在20标准前为了区分这些特性是否生效只能判断C++标准。20标准为这些语言和程序库的功能特性定义了一组预处理器宏，使之成为检测这些功能特性是否存在的一种简单且可移植的方式。测试宏展开会得到该语言、库特性添加到标准草案中的年份和月份，如果该特性有显着变更，宏展开的时间也为更新。- 属性测试宏
__has_cpp_attribute( 属性记号 )
宏函数，用以检测属性是否支持，如：
#if __has_cpp_attribute(nodiscard) > 201603L
#pragma message("nodiscard version is c++20")
#endif- 语言特性测试宏
用以检测当前某个语言功能特性是否支持，单个宏，如：
#if __cpp_concepts >= 201907L
#pragma message("support concepts")
#endif- 标准库特性测试宏
用以检测当前某个标准库特性是否支持，单个宏，不由编译器预定义，由头文件定义：
#ifdef __cpp_lib_bitops
#pragma message("support bitops")
#endif

**三路比较和比较操作符的默认**
新增三路比较运算符，又称spaceship operator，其形式为：
左操作数  右操作数
表达式返回一个对象，使得- 如果 a  b)  b，那幺 (a  b) > 0- 而如果 a 和 b 相等/等价，那幺 (a  b) == 0。
三路比较操作符会作为 >=四个操作符的重写候选，若决议选择了带参数顺序的operator，则对于操作如x @ y，执行 x  y @ 0，对于不带参数顺序的执行 0 @ x  y。
新增可以将比较操作符显式预置=default来要求编译器为某个类生成对应比较，比如：
struct Point
{
int x;
int y;
auto operator(const Point&) const = default;
};

**聚合体指派初始化**
聚合体初始化的语法糖，在c++11的聚合体初始化基础上，增加了可以指派具体值的语法：
struct U {
int a;
float b;
};
U u1{ 1, 2.0 };
U u2{ .a = 1, .b = 2.0 };

**范围for中的初始化语句和初始化器**
17标准中给if和switch语句加了初始化语句，20标准则给基于范围的for加了初始化语句：
std::initializer_list il{ 1, 2, 3 };
for (size_t index{ 0 }; auto& i : il) {
std::cout 

**UTF8字符基础类型char8_t**
新增了基础类型char8_t用以表示UTF8字符，与11标准中的char16_t、char32_t一样同为语言关键字，char8_t的出现主要是为了和旧有的char区分，专门用于表示utf8字符。相对应的标准库``增加了std::u8string的别名，以下来自gcc13：
#if __cplusplus >= 201703L && _GLIBCXX_USE_CXX11_ABI
#include 
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace pmr {
template>
using basic_string = std::basic_string>;
using string    = basic_string;
#ifdef _GLIBCXX_USE_CHAR8_T
using u8string  = basic_string;
#endif
using u16string = basic_string;
using u32string = basic_string;
using wstring   = basic_string;
} // namespace pmr
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // C++17

**新属性**

**= no_unique_address =**
该属性适用于非位域非静态数据成员，指示编译器可以优化当前成员使其与其他非静态数据成员重叠，减少内存占用。如果该成员为空类型（不具有数据成员），则编译器优化为不占空间；如果该成员不为空，则其尾随填充空间可被其他数据成员占用。两个场景例子：
```cpp
struct Empty {};
struct WithEmpty {
int32_t x;  // 4字节
Empty e;  // 1字节（填充至对齐）
};  // 总大小8（4+1+3填充）
struct WithEmptyAttri {
int32_t x;  // 4字节
no_unique_address Empty e;  // 优化为不占空间
};  // 总大小4字节
struct NonEmpty {
int32_t x;  // 4 字节
no_unique_address char y;  // 1字节，无法优化，仍需对齐，*注意这里指定了no_unique_address
// 3 字节填充
};  // 总大小8字节
struct WithNonEmpty {
NonEmpty ne;  // 8 字节，未指定no_unique_address，z不可复用ne空间
char z;  // 1字节, 需对齐
// 3 字节填充
};  // 总大小12字节
struct Optimized {
no_unique_address NonEmpty ne;  // 8 字节
char z;  // 可以复用 ne 的尾随填充
};
int main() {
static_assert(sizeof(WithEmpty) == 8);
static_assert(sizeof(WithEmptyAttri) == 4);
static_assert(sizeof(NonEmpty) == 8);
static_assert(sizeof(WithNonEmpty) == 12);
static_assert(sizeof(Optimized) == 8);
WithNonEmpty wn;
assert(&wn.z == &wn.ne.y + 4);
Optimized o;
assert(&o.z == &o.ne.y + 1);
return 0;
}

**= likely与unlikely =**
现代cpu有指令预取和分支预测功能，在gcc以往也有__builtin_expect，相对应的20标准新增了likely和unlikely属性作为c++标准的分支预测优化属性，用于给程序员协助编译器完成分支预测。

**注释**

**另见**
C++
*C++23
*C++17
*C++14
*C++11
category:C++