c++20 新增的一些小的特性

发布一下 0 0

c++20新增的特性非常多,其中concept,coroutine,module和range为四大特性,在之前的章节中已有涉及,本文则对其他的一些小改动进行讲解。

  • c++20的新增的小的特性std::formatCalendartimezonestd::source_locationstd::span航天飞机运算符 <=>std::endian判断大小端std::remove_cvrefbind_frontstd::atomic_refstd::map<Key,T,Compare,Allocator>::containsstd::barrierstd::latch锁存器std::counting_semaphorestring::starts_with / ends_withstd::sizestd::is_bounded_array_v和std::is_unbounded_arraystd::erase_ifMathematical constantsstd::midpointstd::lerp

std::format

c++20支持新的字符串格式化方式std::format

#include <format>#include <iostream>  using namespace std;  int main(){    // Declare variables    int num = 42;    std::string name = "John";      // Use std::format to format a string with placeholders    // for variables    std::string formatted_str = std::format(        "My name is {} and my favorite number is {}", name,        num);      // Print formatted string to console    std::cout << formatted_str << std::endl;      return 0;}

Calendar

#include <chrono>int main(){    using namespace std::chrono;    // Get a local time_point with system_clock::duration precision    auto now = zoned_time{current_zone(), system_clock::now()}.get_local_time();    // Get a local time_point with days precision    auto ld = floor<days>(now);    // Convert local days-precision time_point to a local {y, m, d} calendar    year_month_day ymd{ld};    // Split time since local midnight into {h, m, s, subseconds}    hh_mm_ss hms{now - ld};    // This part not recommended.  Stay within the chrono type system.    int year{ymd.year()};    int month = unsigned{ymd.month()};    int day = unsigned{ymd.day()};    int hour = hms.hours().count();    int minute = hms.minutes().count();    int second = hms.seconds().count();}

timezone

时区工具

#include <chrono>#include <iostream> int main() {    const std::chrono::zoned_time cur_time{ std::chrono::current_zone(),                                            std::chrono::system_clock::now() };    std::cout << cur_time << '\n';}

std::source_location

文件位置的工具

#include <iostream>#include <source_location>#include <string_view> void log(const std::string_view message,         const std::source_location location =               std::source_location::current()){    std::clog << "file: "              << location.file_name() << '('              << location.line() << ':'              << location.column() << ") `"              << location.function_name() << "`: "              << message << '\n';} template<typename T>void fun(T x){    log(x);} int main(int, char*[]){    log("Hello world!");    fun("Hello C++20!");}

std::span

过去如果一个函数想接受无法确定数组长度的数组作为参数,那么一定需要声明两个参数:数组指针和长度:

void set_data(int *arr, int len) {}int main(){    int buf[128]{ 0 };    set_data(buf, 128);}

这种人工输入增加了编码的风险,数组长度的错误输入会引发程序的未定义行为,甚至是成为可被利用的漏洞。C++20标准库为我们提供了一个很好解决方案std::span,通过它可以定义一个基于连续序列对象的视图,包括原生数组,并且保留连续序列对象的大小。例如:

#include <iostream>#include <span>void set_data(std::span<int> arr) {    std::cout << arr.size();}int main(){    int buf[128]{ 0 };    set_data(buf);}

航天飞机运算符 <=>

(a <=> b) < 0 if a < b,(a <=> b) > 0 if a > b,(a <=> b) == 0 if a and b are equal/equivalent.
#include <compare>#include <iostream> int main(){    double foo = -0.0;    double bar = 0.0;     auto res = foo <=> bar;     if (res < 0)        std::cout << "-0 is less than 0";    else if (res > 0)        std::cout << "-0 is greater than 0";    else if (res == 0)        std::cout << "-0 and 0 are equal";    else        std::cout << "-0 and 0 are unordered";}

std::endian判断大小端

#include <bit>#include <iostream> int main(){    if constexpr (std::endian::native == std::endian::big)        std::cout << "big-endian\n";    else if constexpr (std::endian::native == std::endian::little)        std::cout << "little-endian\n";    else        std::cout << "mixed-endian\n";}

std::remove_cvref

去除cv和引用

#include <type_traits> int main(){    static_assert(std::is_same_v<std::remove_cvref_t<int>, int>);    static_assert(std::is_same_v<std::remove_cvref_t<int&>, int>);    static_assert(std::is_same_v<std::remove_cvref_t<int&&>, int>);    static_assert(std::is_same_v<std::remove_cvref_t<const int&>, int>);    static_assert(std::is_same_v<std::remove_cvref_t<const int[2]>, int[2]>);    static_assert(std::is_same_v<std::remove_cvref_t<const int(&)[2]>, int[2]>);    static_assert(std::is_same_v<std::remove_cvref_t<int(int)>, int(int)>);}

bind_front

和std::bind是一个系列的方法,bind_front可以绑定前n个参数而不用敲placeholder。

#include <functional>#include <iostream>int main(){    auto calc=[](int a, int b, int c) { return a+b-c;};        auto aa = std::bind_front(calc, 1,2);    std::cout << aa (3)<<"\n";    auto bb = std::bind_front(calc, 1,2,3);    std::cout << bb ()<<"\n";    auto cc=std::bind(calc, 1,std::placeholders::_2,std::placeholders::_1);    std::cout<<cc(3,2)<<"\n";       auto dd=std::bind(calc, std::placeholders::_1,std::placeholders::_2,3);    std::cout<<dd(1,2); }

std::atomic_ref

原子引用

在下面的例子中,最终将打印100

#include <atomic>#include <thread>#include <vector>#include <iostream>int do_count(int value){   std::atomic<int> counter { value };   std::vector<std::thread> threads;   for (int i = 0; i < 10; ++i)   {      threads.emplace_back([&counter]() {         for (int i = 0; i < 10; ++i)         {            ++counter;            {               using namespace std::chrono_literals;               std::this_thread::sleep_for(50ms);            }         }      });   }   for (auto& t : threads) t.join();   return counter;}int main(){   int result = do_count(0);   std::cout << result << '\n'; // prints 100}

std::atomic并作用于引用不生效, 下面的例子将打印0。

#include <atomic>#include <thread>#include <vector>#include <iostream>void do_count_ref(int& value){   std::atomic<int> counter{ value };   std::vector<std::thread> threads;   for (int i = 0; i < 10; ++i)   {      threads.emplace_back([&counter]() {         for (int i = 0; i < 10; ++i)         {            ++counter;            {               using namespace std::chrono_literals;               std::this_thread::sleep_for(50ms);            }         }         });   }   for (auto& t : threads) t.join();}int main(){   int value = 0;   do_count_ref(value);   std::cout << value << '\n'; // prints 0}

使用atimic_ref进行修改,可以打印100。

#include <atomic>#include <thread>#include <vector>#include <iostream>void do_count_ref(int& value){   std::atomic_ref<int> counter{ value };   std::vector<std::thread> threads;   for (int i = 0; i < 10; ++i)   {      threads.emplace_back([&counter]() {         for (int i = 0; i < 10; ++i)         {            ++counter;            {               using namespace std::chrono_literals;               std::this_thread::sleep_for(50ms);            }         }         });   }   for (auto& t : threads) t.join();}int main(){   int value = 0;   do_count_ref(value);   std::cout << value << '\n'; // prints 0}

std::map<Key,T,Compare,Allocator>::contains

map新增contains方法,在此之前使用的是find或者count。

#include <iostream>#include <map> int main(){    std::map<int,char> example = {{1,'a'},{2,'b'}};     for(int x: {2, 5}) {        if(example.contains(x)) {            std::cout << x << ": Found\n";        } else {            std::cout << x << ": Not found\n";        }    }}

std::barrier

线程屏障

#include <barrier>#include <iostream>#include <string>#include <thread>#include <vector> int main(){    const auto workers = { "Anil", "Busara", "Carl" };     auto on_completion = []() noexcept    {        // locking not needed here        static auto phase = "... done\n" "Cleaning up...\n";        std::cout << phase;        phase = "... done\n";    };     std::barrier sync_point(std::ssize(workers), on_completion);     auto work = [&](std::string name)    {        std::string product = "  " + name + " worked\n";        std::cout << product;  // ok, op<< call is atomic        sync_point.arrive_and_wait();         product = "  " + name + " cleaned\n";        std::cout << product;        sync_point.arrive_and_wait();    };     std::cout << "Starting...\n";    std::vector<std::jthread> threads;    threads.reserve(std::size(workers));    for (auto const& worker : workers)        threads.emplace_back(work, worker);}

std::latch锁存器

latch = single-use barrier.

#include <functional>#include <iostream>#include <latch>#include <string>#include <thread> int main() {    struct job {        const std::string name;        std::string product{"not worked"};        std::thread action{};    } jobs[] = {{"annika"}, {"buru"}, {"chuck"}};        std::latch work_done{std::size(jobs)};    std::latch start_clean_up{1};        auto work = [&](job& my_job) {        my_job.product = my_job.name + " worked";        work_done.count_down();        start_clean_up.wait();        my_job.product = my_job.name + " cleaned";    };        std::cout << "Work starting... ";    for (auto& job : jobs) {        job.action = std::thread{work, std::ref(job)};    }    work_done.wait();    std::cout << "done:\n";    for (auto const& job : jobs) {        std::cout << "  " << job.product << '\n';    }        std::cout << "Workers cleaning up... ";    start_clean_up.count_down();    for (auto& job : jobs) {        job.action.join();    }    std::cout << "done:\n";    for (auto const& job : jobs) {        std::cout << "  " << job.product << '\n';    }}

std::counting_semaphore

计数信号量

#include <chrono>#include <iostream>#include <semaphore>#include <thread> // global binary semaphore instances// object counts are set to zero// objects are in non-signaled statestd::binary_semaphore    smphSignalMainToThread{0},    smphSignalThreadToMain{0}; void ThreadProc(){    // wait for a signal from the main proc    // by attempting to decrement the semaphore    smphSignalMainToThread.acquire();     // this call blocks until the semaphore's count    // is increased from the main proc     std::cout << "[thread] Got the signal\n"; // response message     // wait for 3 seconds to imitate some work    // being done by the thread    using namespace std::literals;    std::this_thread::sleep_for(3s);     std::cout << "[thread] Send the signal\n"; // message     // signal the main proc back    smphSignalThreadToMain.release();} int main(){    // create some worker thread    std::thread thrWorker(ThreadProc);     std::cout << "[main] Send the signal\n"; // message     // signal the worker thread to start working    // by increasing the semaphore's count    smphSignalMainToThread.release();     // wait until the worker thread is done doing the work    // by attempting to decrement the semaphore's count    smphSignalThreadToMain.acquire();     std::cout << "[main] Got the signal\n"; // response message    thrWorker.join();}

string::starts_with / ends_with

字符串开头/结尾

#include <iostream>#include <string>#include <string_view> template<typename PrefixType>void test_prefix_print(const std::string& str, PrefixType prefix){    std::cout << '\'' << str << "' starts with '" << prefix << "': "              << str.starts_with(prefix) << '\n';} int main(){    std::boolalpha(std::cout);    auto helloWorld = std::string("hello world");     test_prefix_print(helloWorld, std::string_view("hello"));     test_prefix_print(helloWorld, std::string_view("goodbye"));     test_prefix_print(helloWorld, 'h');     test_prefix_print(helloWorld, 'x');}

std::size

size的common方法

#include <iostream>#include <vector> int main(){    std::vector<int> v{3, 1, 4};    std::cout << std::size(v) << '\n';     int a[]{-5, 10, 15};    std::cout << std::size(a) << '\n';     // since C++20 the signed size (ssize) is available    auto i = std::ssize(v);    for (--i; i != -1; --i)        std::cout << v[i] << (i ? ' ' : '\n');    std::cout << "i = " << i << '\n';}

std::is_bounded_array_v和std::is_unbounded_array

#include <iostream>#include <type_traits> #define OUT(...) std::cout << #__VA_ARGS__ << " : " << __VA_ARGS__ << '\n' class A {}; int main(){    std::cout << std::boolalpha;    OUT(std::is_bounded_array_v<A>);    OUT(std::is_bounded_array_v<A[]>);    OUT(std::is_bounded_array_v<A[3]>);    OUT(std::is_bounded_array_v<float>);    OUT(std::is_bounded_array_v<int>);    OUT(std::is_bounded_array_v<int[]>);    OUT(std::is_bounded_array_v<int[3]>);}
#include <iostream>#include <type_traits> #define OUT(...) std::cout << #__VA_ARGS__ << " : " << __VA_ARGS__ << '\n' class A {}; int main(){    std::cout << std::boolalpha;    OUT( std::is_unbounded_array_v<A> );    OUT( std::is_unbounded_array_v<A[]> );    OUT( std::is_unbounded_array_v<A[3]> );    OUT( std::is_unbounded_array_v<float> );    OUT( std::is_unbounded_array_v<int> );    OUT( std::is_unbounded_array_v<int[]> );    OUT( std::is_unbounded_array_v<int[3]> );}

std::erase_if

按照条件erase数据

#include <iostream>#include <numeric>#include <string_view>#include <vector> void print_container(std::string_view comment, const std::vector<char>& c){    std::cout << comment << "{ ";    for (auto x : c)        std::cout << x << ' ';    std::cout << "}\n";} int main(){    std::vector<char> cnt(10);    std::iota(cnt.begin(), cnt.end(), '0');    print_container("Initially, cnt = ", cnt);     std::erase(cnt, '3');    print_container("After erase '3', cnt = ", cnt);     auto erased = std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });    print_container("After erase all even numbers, cnt = ", cnt);    std::cout << "Erased even numbers: " << erased << '\n';}

对比之前的remove_if和erase

#include <iostream>#include <vector>#include <algorithm>bool isEven(int n) // 是否是偶数{    return n % 2 == 0;}int main(){    std::vector<int> vecTest;    for (int i = 0; i < 10; ++i)        vecTest.push_back(i);    for (int i = 0; i < vecTest.size(); ++i)        std::cout << vecTest[i] << " ";    std::cout << std::endl;    // 移动元素    std::vector<int>::iterator itor = std::remove_if(vecTest.begin(), vecTest.end(), isEven);    // 查看移动后的变化    for (int i = 0; i < vecTest.size(); ++i)        std::cout << vecTest[i] << " ";    std::cout << std::endl;    // 删除元素    vecTest.erase(itor, vecTest.end());    for (int i = 0; i < vecTest.size(); ++i)        std::cout << vecTest[i] << " ";    return 0;}

Mathematical constants

c++20新增了一些数学常量

#include <numbers>#include <iostream>int main() {    std::cout << std::numbers::log2e_v<double> << std::endl;}

#std::midpoint

#include <cstdint>#include <iostream>#include <limits>#include <numeric> int main(){    std::uint32_t a = std::numeric_limits<std::uint32_t>::max();    std::uint32_t b = std::numeric_limits<std::uint32_t>::max() - 2;     std::cout << "a: " << a << '\n'              << "b: " << b << '\n'              << "Incorrect (overflow and wrapping): " << (a + b) / 2 << '\n'              << "Correct: " << std::midpoint(a, b) << "\n\n";     auto on_pointers = [](int i, int j)    {        char const* text = "0123456789";        char const* p = text + i;        char const* q = text + j;        std::cout << "std::midpoint('" << *p << "', '" << *q << "'): '"                  << *std::midpoint(p, q) << "'\n";    };     on_pointers(2, 4);    on_pointers(2, 5);    on_pointers(5, 2);    on_pointers(2, 6);}

std::lerp

线性计算

a+t(b-a)

#include <cassert>#include <cmath>#include <iostream> float naive_lerp(float a, float b, float t){    return a + t * (b - a);} int main(){    std::cout << std::boolalpha;     const float a = 1e8f, b = 1.0f;    const float midpoint = std::lerp(a, b, 0.5f);     std::cout << "a = " << a << ", " << "b = " << b << '\n'              << "midpoint = " << midpoint << '\n';     std::cout << "std::lerp is exact: "              << (a == std::lerp(a, b, 0.0f)) << ' '              << (b == std::lerp(a, b, 1.0f)) << '\n';     std::cout << "naive_lerp is exact: "              << (a == naive_lerp(a, b, 0.0f)) << ' '              << (b == naive_lerp(a, b, 1.0f)) << '\n';     std::cout << "std::lerp(a, b, 1.0f) = " << std::lerp(a, b, 1.0f) << '\n'              << "naive_lerp(a, b, 1.0f) = " << naive_lerp(a, b, 1.0f) << '\n';     assert(not std::isnan(std::lerp(a, b, INFINITY))); // lerp here can be -inf     std::cout << "Extrapolation demo, given std::lerp(5, 10, t):\n";    for (auto t{-2.0}; t <= 2.0; t += 0.5)        std::cout << std::lerp(5.0, 10.0, t) << ' ';    std::cout << '\n';}

版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除

本文地址:http://0561fc.cn/211193.html