title: C++20 新增特性.md
toc: true
date: 2021-12-22 09:25:00


C++20 新增特性

https://zhuanlan.zhihu.com/p/137646370

![](C++20 新增特性/1.jpg)

原文链接:

新增关键字(keywords)

新增标识符(Identifies)

模块(Modules)

优点

创建模块

// cppcon.cpp 
export module cppcon; 
namespace CppCon { 
    auto GetWelcomeHelper() {  return "Welcome to CppCon 2019!";  } 
    export auto GetWelcome() { return GetWelcomeHelper();} 
}

引用模块

// main.cpp 
import cppcon; 
int main(){ 
    std::cout << CppCon::GetWelcome(); 
}

import 头文件

Ranges

Ranges 是什么 ?

好处:

vector<int> data{11, 22, 33}; 
sort(begin(data), end(data)); 
sort(data); // 使用 Ranges

相关功能

例子

vector<int> data {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
auto result = data | 
              views::remove_if([](int i) { return i % 2 == 1;}) | 
              views::transform([](int i) { return to_string(i);}); 
// result = {"2", "4", "6", "8", "10" };
// 注意 以上操作被延迟, 当你遍历result的时候才触发
vector<int> data{4, 3, 4, 1, 8, 0, 8}; 
vector<int> result = data | actions::sort | actions::unique;
int total = accumulate (
                        view::ints(1) | 
                        view::transform([](int i) {return i * i;}) | 
                        view::take(10), 
                        0);

协程(Coroutines)

什么是协程

for co_await (for-range-declaration: expression) statement

用处

例子(VC++)

experimental::generator<int> GetSequenceGenerator( 
    int startValue, 
    size_t numberOfValues) { 
    for (int i = 0 startValue; i < startValue + numberOfValues; ++i){ 
        time_t t = system_clock::to_time_t(system_clock::now()); 
        cout << std:: ctime(&t); co_yield i; 
    } 
} 
int main() {
    auto gen = GetSequenceGenerator(10, 5); 
    for (const auto& value : gen) { 
        cout << value << "(Press enter for next value)" << endl; 
        cin.ignore(); 
    } 
}

Concepts

如何定义

template<typename T> concept Incrementable = requires(T x) {x++; ++x;};

使用

template<Incrementable T> 
void Foo(T t); 

template<typename T> requires Incrementable<T> 
void Foo(T t); 

template<typename T> 
void Foo(T t) requires Incrementable<T>; 

void Foo(Incrementable auto t);

例子

template <typename T> concept HasSize = requires (T x){ 
    {x.size()} -> std::convertible_to<std::size_t>; 
};
template<typename T> 
requires Incrementable<T> && Decrementable<T> 
void Foo(T t); 
// or 
template<typename T> 
concept Incr_Decrementable = Incrementable<T> && Decrementable<T>; 

template<Incr_Decrementable T> 
void Foo(T t);

Lambda 表达式的更新

[=, this] 需要显式捕获this变量

模板形式的 Lambda 表达式

可以在lambda表达式中使用模板语法

[]template<T>(T x) {/* ... */}; 
[]template<T>(T* p) {/* ... */}; 
[]template<T, int N>(T (&a)[N]) {/* ... */};

原因1

auto func = [](auto vec){ 
    using T = typename decltype(vec)::value_type; 
}
auto func = []<typename T>(vector<T> vec){ 
    // ... 
}

原因2: 方便获取通用lambda形参类型, 访问静态函数

auto func = [](auto const& x){ 
    using T = std::decay_t<decltype(x)>; 
    T copy = x; T::static_function(); 
    using Iterator = typename T::iterator; 
}
auto func = []<typename T>(const T& x){ 
    T copy = x; T::static_function(); 
    using Iterator = typename T::iterator; 
}

原因3: 完美转发

auto func = [](auto&& ...args) { 
    return foo(std::forward<decltype(args)>(args)...); 
}
auto func = []<typename …T>(T&& …args){ 
    return foo(std::forward(args)...); 
}

Lambda 表达式捕获支持打包展开(Pack Expansion)

template<class F, class... Args> 
auto delay_invoke(F f, Args... args){ 
    return [f, args...]{ 
        return std::invoke(f, args...); 
    } 
}
template<class F, class... Args> 
auto delay_invoke(F f, Args... args){ 
    // Pack Expansion:  args = std::move(args)...  
    return [f = std::move(f), args = std::move(args)...](){ 
        return std::invoke(f, args...); 
    } 
}

常量表达式(constexpr) 的更新

constexpr string & vector

原子(Atomic)智能指针

例子

template<typename T> 
class concurrent_stack { 
    struct Node { 
        T t; 
        shared_ptr<Node> next; 
    }; 
    atomic_shared_ptr<Node> head; 
    // C++11: 去掉 "atomic_" 并且在访问时, 需要用 
    // 特殊的函数控制线程安全, 例如用std::tomic_load 
public: 
    class reference { 
        shared_ptr<Node> p; 
        <snip> 
    }; 
    auto find(T t) const { 
        auto p = head.load(); // C++11: atomic_load(&head) 
        while (p && p->t != t) 
            p = p->next; 
        return reference(move(p)); 
    } 
    auto front() const { 
        return reference(head); 
    } 
    void push_front(T t) { 
        auto p = make_shared<Node>(); 
        p->t = t; p->next = head; 
        while (!head.compare_exchange_weak(p->next, p)){ 
    } // C++11: atomic_compare_exchange_weak(&head, &p->next, p); }     
    void pop_front() { 
        auto p = head.load(); 
        while (p && !head.compare_exchange_weak(p, p->next)) {
        } // C++11: atomic_compare_exchange_weak(&head, &p, p->next); 
    } 
};

例子来自 Herb Sutter 的 N4162 论文

自动合流(Joining), 可中断(Cancellable) 的线程

例子

std::thread 在析构函数中如果线程 joinable() 会直接调用 std::terminate() 直接导致程序退出

void DoWorkPreCpp20() { 
    std::thread job([] { /* ... */ }); 
    try { 
        // ... Do something else ... 
    } catch (...) { 
        job.join(); 
        throw; // rethrow 
    } 
    job.join(); 
} 

void DoWork() { 
    std::jthread job([] { /* ... */ }); 
    // ... Do something else ... 
} // jthread destructor automatically calls join()
std::jthread job([](std::stop_token token) {
    while (!token.stop_requested()) { 
        //... 
    } 
}); 
//... job.request_stop(); 
// auto source = job.get_stop_source() 
// auto token = job.get_stop_token()

C++20 同步(Synchronization)库

std::atomic_ref

其他更新

指定初始化(Designated Initializers)

struct Data { 
    int anInt = 0; 
    std::string aString; 
}; 
Data d{ .aString = "Hello" };

航天飞机操作符 <=>

示例:

class Point { 
    int x; int y; 
public: 
    friend bool operator==(const Point& a, const Point& b){ 
        return a.x==b.x && a.y==b.y; 
    } 
    friend bool operator< (const Point& a, const Point& b){ 
        return a.x < b.x || (a.x == b.x && a.y < b.y);  
    } 
    friend bool operator!=(const Point& a, const Point& b) { 
        return !(a==b); 
    } 
    friend bool operator<=(const Point& a, const Point& b) { 
        return !(b<a); 
    } 
    friend bool operator> (const Point& a, const Point& b) { 
        return b<a; 
    } 
    friend bool operator>=(const Point& a, const Point& b) { 
        return !(a<b); 
    } 
    // ... 其他非比较函数 ... 
}; 
#include <compare> class Point { 
    int x; int y; 
public: 
    auto operator<=>(const Point&)  const = default; // 比较操作符自动生成
    // ... 其他非比较函数 ... 
};

范围 for 循环语句支持初始化语句

struct Foo { 
    int value; int result; 
}; 
Foo GetData() { 
    return Foo(); 
} 

int main() { 
    switch (auto data = GetData(); data.value) { 
    case 1: 
        return data.result; 
    } 
}
struct Foo { 
    int value; int result; 
}; 
Foo* GetData() {
  return new Foo(); 
} 

int main() { 
    if (auto data = GetData(); data) { 
        // Use 'data’ 
    } 
}
struct Foo { 
    std::vector<int> values; 
}; 
Foo GetData() { 
    return Foo(); 
} 
int main() { 
    for (auto data = GetData(); 
    auto& value : data.values) { 
        // Use 'data’ 
    } 
}
 

非类型模板形参支持字符串

template<auto& s> void DoSomething() { 
    std::cout << s << std::endl; 
} 
int main() { 
    DoSomething<"CppCon">(); 
}

[[likely]], [[unlikely]]

先验概率指导编译器优化

switch (value) { 
    case 1: break; 
    [[likely]] case 2: break; 
    [[unlikely]] case 3: break; 
}

日历(Calendar)和时区(Timezone)功能

// creating a year 
auto y1 = year{ 2019 }; 
auto y2 = 2019y; 
// creating a mouth
auto m1 = month{ 9 }; 
auto m2 = September; 
// creating a day 
auto d1 = day{ 18 }; 
auto d2 = 18d;
year_mouth_day fulldate1{2019y, September, 18d}; 
auto fulldate2 = 2019y / September / 18d; 
year_mouth_day fulldate3{Monday[3]/September/2019}; // Monday[3] 表示第三个星期一
using days = duration<signed interger type of at least 25bits, 
                      ratio_multiply<ratio<24>, hours::period>>; 
using weeks = ...; using mouths = ...; 
using years = ...;
weeks w{1}; // 1 周 
days d{w}; // 将 1 周 转换成天数
template<class Duration> 
using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>; 
using sys_seconds = sys_time<std::chrono::seconds>; 
using sys_days = sys_time<std::chrono::days>; 
// 用例: 
system_clock::time_point t =  sys_days{ 2019y / September / 18d }; // date -> time_point 
auto yearmonthday =  year_month_day{ floor<days>(t) }; // time_point -> date
auto t = sys_days{2019y/September/18d} + 9h + 35min + 10s; // 2019-09-18 09:35:10 UTC
// Convert UTC to Denver 
time: zoned_time denver = { "America/Denver", t }; 
// Construct a local time in Denver: 
auto t = zoned_time{ 
    "America/Denver",  local_days{Wednesday[3] / September / 2019} + 9h 
}; 
// Get current local time: 
auto t = zoned_time{ current_zone(), system_clock::now() };

std::span

int data[42]; span<int, 42> a {data}; // fixed-size: 42 ints 
span<int> b {data}; // dynamic-size: 42 ints 
span<int, 50> c {data}; // compilation error 
span<int> d{ ptr, len }; // dynamic-size: len ints

特性测试宏

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

包含 C++ 标准库版本, 发布日期, 版权证书, 特性宏等

consteval 函数

constexpr 函数可能编译期执行, 也可以在运行期执行, consteval 只能在编译器执行, 如果不满足要求编译不通过

constinit

强制指定以常量方式初始化

const char* GetStringDyn() { 
    return "dynamic init"; 
} 
constexpr const char* GetString(bool constInit) { 
    return constInit ? 
        "constant init" : 
        GetStringDyn(); 
} 
constinit const char* a = GetString(true); // ✔ 
constinit const char* b = GetString(false); // ❌

using 引用 enum 类型

enum class CardTypeSuit { 
    Clubs, 
    Diamonds, 
    Hearts, 
    Spades 
}; 
std::string_view GetString(const CardTypeSuit cardTypeSuit) { 
    switch (cardTypeSuit) { 
    case CardTypeSuit::Clubs: 
        return "Clubs"; 
    case CardTypeSuit::Diamonds: 
        return "Diamonds"; 
    case CardTypeSuit::Hearts: 
         return "Hearts"; 
    case CardTypeSuit::Spades: 
         return "Spades"; 
    } 
} 
std::string_view GetString(const CardTypeSuit cardTypeSuit) { 
    switch (cardTypeSuit) { 
        using enum CardTypeSuit; // 这里 
        case Clubs: return "Clubs"; 
        case Diamonds: return "Diamonds"; 
        case Hearts: return "Hearts"; 
        case Spades: return "Spades"; 
    } 
}

格式化库(std::format)

不展开, 类似Python 的格式化,

std::string s = std::format("Hello CppCon {}!", 2019);

增加数学常量

再也不用为 M_PI 发愁啦

std::source_location

用于获取代码位置, 对于日志和错误信息尤其有用

[[nodiscard(reason)]]

表明返回值不可抛弃, 加入理由的支持

[[nodiscard("Ignoring the return value will result in memory leaks.")]] 
void* GetData() { /* ... */ }

位运算

加入循环移位, 计数0和1位等功能

一些小更新

std::string str = "Hello world!"; 
bool b = str.starts_with("Hello"); // starts_with, ends_with 
std::map myMap{ std::pair{1, "one"s}, {2, "two"s}, {3, "three"s} }; 
bool result = myMap.contains(2); // contains, 再也不用  .find() == .end() 了

参考资料