C++ 20 语言特性
本文为译文,原文链接:https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP20.md
Coroutines(协程)
协程是一种特殊的函数,它的执行可以被暂停或恢复。要定义协程,关键字co_return
,co_await
,或co_yield
必须出现在函数体中。
协程的一个例子是
generator<int> range(int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for (int n : range(0, 10)) {
std::cout << n << std::endl;
}
上面的range
生成器函数生成的值从start
开始直到end
start
中的当前值。生成器在每次调用range
时都保持它的状态co_yield
接受给定的表达式,生成co_yield
之后继续执行。
协程的另一个例子是
task<void> echo(socket s) {
for (;;) {
auto data = co_await s.async_read();
co_await async_write(s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
在本例中,引入了co_await
关键字。这个关键字接受一个表达式,如果您正在等待的东西co_yield
使用co_await
。)
使用任务惰性地评估一个值
task<int> calculate_meaning_of_life() {
co_return 42;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
注意generator
和task
类,所以我使用
Concepts(概念)
概念被命名为约束类型的编译时谓词。它们的形式如下
template < template-parameter-list >
concept concept-name = constraint-expression;
其中constraint-expression
计算为
// `T` is not limited by any constraints.
template <typename T>
concept always_satisfied = true;
// Limit `T` to integrals.
template <typename T>
concept integral = std::is_integral_v<T>;
// Limit `T` to both the `integral` constraint and signedness.
template <typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint.
template <typename T>
concept unsigned_integral = integral<T> && !signed_integral<T>;
有各种各样的语法形式来加强概念
// Forms for function parameters:
// `T` is a constrained type template parameter.
template <my_concept T>
void f(T v);
// `T` is a constrained type template parameter.
template <typename T>
requires my_concept<T>
void f(T v);
// `T` is a constrained type template parameter.
template <typename T>
void f(T v) requires my_concept<T>;
// `v` is a constrained deduced parameter.
void f(my_concept auto v);
// `v` is a constrained non-type template parameter.
template <my_concept auto v>
void g();
// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
my_concept auto foo = ...;
// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<my_concept T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []<typename T> requires my_concept<T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []<typename T> (T v) requires my_concept<T> {
// ...
};
// `v` is a constrained deduced parameter.
auto f = [](my_concept auto v) {
// ...
};
// `v` is a constrained non-type template parameter.
auto g = []<my_concept auto v> () {
// ...
};
requires
关键字可以用来启动一个
template <typename T>
requires my_concept<T> // `requires` clause.
void f(T);
template <typename T>
concept callable = requires (T f) { f(); }; // `requires` expression.
template <typename T>
requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add(T a, T b) {
return a + b;
}
注意,
Simple requirements - 断言给定表达式是否有效。
template <typename T>
concept callable = requires (T f) { f(); };
Type requirements - 关键字typename
后跟一个类型名表示,断言给定的类型名是有效的。
struct foo {
int foo;
};
struct bar {
using value = int;
value data;
};
struct baz {
using value = int;
value data;
};
// Using SFINAE, enable if `T` is a `baz`.
template <typename T, typename = std::enable_if_t<std::is_same_v<T, baz>>>
struct S {};
template <typename T>
using Ref = T&;
template <typename T>
concept C = requires {
// Requirements on type `T`:
typename T::value; // A) has an inner member named `value`
typename S<T>; // B) must have a valid class template specialization for `S`
typename Ref<T>; // C) must be a valid alias template substitution
};
template <C T>
void g(T a);
g(foo{}); // ERROR: Fails requirement A.
g(bar{}); // ERROR: Fails requirement B.
g(baz{}); // PASS.
Compound requirements - 用大括号括起来的表达式,后面跟着返回类型或类型约束。
template <typename T>
concept C = requires(T x) {
{*x} -> typename T::inner; // the type of the expression `*x` is convertible to `T::inner`
{x + 1} -> std::same_as<int>; // the expression `x + 1` satisfies `std::same_as<decltype((x + 1))>`
{x * 1} -> T; // the type of the expression `x * 1` is convertible to `T`
};
Nested requirements - 由requires
关键字表示,指定额外的约束( 例如本地参数参数) 。
template <typename T>
concept C = requires(T x) {
requires std::same_as<sizeof(x), size_t>;
};
Designated initializers( 指定初始化式)
struct A {
int x;
int y;
int z = 123;
};
A a {.x = 1, .z = 2}; // a.x == 1, a.y == 0, a.z == 2
Template syntax for lambdas(lambda 的模板语法)
在
auto f = []<typename T>(std::vector<T> v) {
// ...
};
Range-based for loop with initializer( 带初始化器的基于范围的for 循环)
该特性简化了常见的代码模式,有助于保持范围紧凑,并为常见的生存期问题提供了优雅的解决方案。
for (std::vector v{1, 2, 3}; auto& e : v) {
std::cout << e;
}
// prints "123"
likely and unlikely attributes( 可能和不可能的属性)
向优化器提供提示,说明已标记语句执行的概率很高。
switch (n) {
case 1:
// ...
break;
[[likely]] case 2: // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break;
}
如果一个可能
int random = get_random_number_between_x_and_y(0, 3);
if (random > 0) [[likely]] {
// body of if statement
// ...
}
它也可以应用于迭代语句的子语句
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
Deprecate implicit capture of this( 不建议隐式捕获)
在[=]
隐式捕获this
现在已弃用[=,this]
或[=,*this]
显式捕获。
struct int_value {
int n = 0;
auto getter_fn() {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, *this]() { return n; };
}
};
Class types in non-type template parameters( 非类型模板形参中的类型)
类现在可以在非类型模板参数中使用。作为模板参数传入的对象的类型为const T
,其中T
是对象的类型,并且具有静态存储时间。
struct foo {
foo() = default;
constexpr foo(int) {}
};
template <foo f>
auto get_foo() {
return f;
}
get_foo(); // uses implicit constructor
get_foo<foo{123}>();
constexpr virtual functions(constexpr 虚函数)
虚函数现在可以是constexpr
并在编译时计算。constexpr
虚函数可以覆盖非constexpr
虚函数,反之亦然。
struct X1 {
virtual int f() const = 0;
};
struct X2: public X1 {
constexpr virtual int f() const { return 2; }
};
struct X3: public X2 {
virtual int f() const { return 3; }
};
struct X4: public X3 {
constexpr virtual int f() const { return 4; }
};
constexpr X4 x4;
x4.f(); // == 4
explicit(bool)( 是否显式)
在编译时有条件地选择构造函数是否显式。explicit(true)
与指定explicit
相同。
struct foo {
// Specify non-integral types (strings, floats, etc.) require explicit construction.
template <typename T>
explicit(!std::is_integral_v<T>) foo(T) {}
};
foo a = 123; // OK
foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c {"123"}; // OK
Immediate functions
类似于constexpr
函数,但是带有consteval
说明符的函数必须产生一个常量。这些被称为“直接函数”。
consteval int sqr(int n) {
return n * n;
}
constexpr int r = sqr(100); // OK
int x = 100;
int r2 = sqr(x); // ERROR: the value of `x` is not usable in a constant expression
// OK if `sqr` were a `constexpr` function
using enum
将枚举成员引入作用域以提高可读性。之前
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string(rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red: return "red";
case rgba_color_channel::green: return "green";
case rgba_color_channel::blue: return "blue";
case rgba_color_channel::alpha: return "alpha";
}
}
之后
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string(rgba_color_channel my_channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red: return "red";
case green: return "green";
case blue: return "blue";
case alpha: return "alpha";
}
}
Lambda capture of parameter pack( 参数包的Lambda 捕获)
捕获参数按值包
template <typename... Args>
auto f(Args&&... args){
// BY VALUE:
return [...args = std::forward<Args>(args)] {
// ...
};
}
通过引用捕获参数包
template <typename... Args>
auto f(Args&&... args){
// BY REFERENCE:
return [&...args = std::forward<Args>(args)] {
// ...
};
}
char8_t
提供表示
char8_t utf8_str[] = u8"\u0123";
c++ 20 库功能
Concepts library
标准库还提供了用于构建更复杂概念的概念。其中包括
核心语言概念
same_as
- 指定两种相同的类型。derived_from
- 指定一个类型派生自另一个类型。convertible_to
- 指定一个类型可隐式转换为另一个类型。common_with
- 指定两个类型共享一个公共类型。integral
- 指定类型为整型。default_constructible
- 指定可以默认构造类型的对象。
Comparison concepts:
boolean
- 指定可在布尔上下文中使用的类型。equality_comparable
- 指定operator==
是一个等价关系。
Object concepts:
movable
- 指定可移动和交换某一类型的对象。copyable
- 指定可复制、移动和交换某一类型的对象。semiregular
- 指定某个类型的对象可以被复制、移动、交换和默认构造。regular
- 指定类型为regular ,即既为semiregular
又为equality_comparable
。
Callable concepts:
invocable
- 指定可使用给定参数类型集调用可调用类型。predicate
- 指定可调用类型是布尔谓词。
Synchronized buffered outputstream
缓冲包装输出流的输出操作,以确保同步
std::osyncstream{std::cout} << "The value of x is:" << x << std::endl;
std::span
void f(std::span<int> ints) {
std::for_each(ints.begin(), ints.end(), [](auto i) {
// ...
});
}
std::vector<int> v = {1, 2, 3};
f(v);
std::array<int, 3> a = {1, 2, 3};
f(a);
// etc.
示例
constexpr size_t LENGTH_ELEMENTS = 3;
int* arr = new int[LENGTH_ELEMENTS]; // arr = {0, 0, 0}
// Fixed-sized span which provides a view of `arr`.
std::span<int, LENGTH_ELEMENTS> span = arr;
span[1] = 1; // arr = {0, 1, 0}
// Dynamic-sized span which provides a view of `arr`.
std::span<int> d_span = arr;
span[0] = 1; // arr = {1, 1, 0}
constexpr size_t LENGTH_ELEMENTS = 3;
int* arr = new int[LENGTH_ELEMENTS];
std::span<int, LENGTH_ELEMENTS> span = arr; // OK
std::span<double, LENGTH_ELEMENTS> span2 = arr; // ERROR
std::span<int, 1> span3 = arr; // ERROR
Bit operations
<bit>
头,它提供了一些位操作,包括
std::popcount(0u); // 0
std::popcount(1u); // 1
std::popcount(0b1111`0000u); // 4
Math constants
在<numbers>
std::numbers::pi; // 3.14159...
std::numbers::e; // 2.71828...
std::is_constant_evaluated
谓词函数,当它在编译时上下文中被调用时为真
constexpr bool is_compile_time() {
return std::is_constant_evaluated();
}
constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false
std::make_shared supports arrays
auto p = std::make_shared<int[]>(5); // pointer to `int[5]`
// OR
auto p = std::make_shared<int[5]>(); // pointer to `int[5]`
starts_with and ends_with on strings
字符串starts_with
和ends_with
成员函数来检查一个字符串是否以给定的字符串开始或结束。
std::string str = "foobar";
str.starts_with("foo"); // true
str.ends_with("baz"); // false
Check if associative container has element
像集合和映射这样的关联容器有一个“contains”成员函数,它可以用来代替“查找和检查迭代器的结束”习惯用法。
std::map<int, char> map {{1, `a`}, {2, `b`}};
map.contains(2); // true
map.contains(123); // false
std::set<int> set {1, 2, 3};
set.contains(2); // true
std::bit_cast
将对象从一种类型重新解释为另一种类型的更安全的方法。
float f = 123.0;
int i = std::bit_cast<int>(f);
std::midpoint
安全地计算两个整数的中点
std::midpoint(1, 3); // == 2
std::to_array
将给定的数组std::array
。
std::to_array("foo"); // returns `std::array<char, 4>`
std::to_array<int>({1, 2, 3}); // returns `std::array<int, 3>`
int a[] = {1, 2, 3};
std::to_array(a); // returns `std::array<int, 3>`