C++20新特性

语言特性

__has_cpp_attribute

  • 检查是否存在由属性标记命名的属性,例如deprecated等
#include 
#ifdef __has_cpp_attribute
    #if __has_cpp_attribute(deprecated)
        #define DEPRECATED(msg) [[deprecated(msg)]]
    #endif
#endif

#ifndef DEPRECATED
    #define DEPRECATED
#endif

DEPRECATED("the func has been deprecated")
void func()
{
    std::cout << "test";
}

int main()
{
    func();

    getchar();
    return 0;
}

operator<=>

  • 用于两个相同的值进行大小相等判断
#include 
#include 
 
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 // (res == 0)
        std::cout << "-0 and 0 are equal";
}
//输出结果:-0 and 0 are equal

聚合初始化

  • 可通过结构体变量名指定初始化
struct test
{
	int a;
	int b;
};
int main()
{
	test t{ .b = 10, .a = 20 };//error
	test t{ .a = 10, .b = 20 };
	return 0;
}

char8_t

  • 新增的宽字符串

[[no_unique_address]]

  • 适用于在不是位字段的非静态数据成员的声明中声明的名称,编译器可以优化它以不占用空间
#include 
 
struct Empty {}; // empty class
 
struct X {
    int i;
    Empty e;
};
 
struct Y {
    int i;
    [[no_unique_address]] Empty e;
};
 
struct Z {
    char c;
    [[no_unique_address]] Empty e1, e2;
};
 
struct W {
    char c[2];
    [[no_unique_address]] Empty e1, e2;
};

[[likely]] & [[unlikely]]

  • 允许编译器针对以下情况进行优化:包含该语句的执行路径比不包含此类语句的任何替代执行路径更有可能
if(x)[[likely]]
{
	std::cout << 1;
}
else[[unlikely]]
{
	std::cout << 2;
}

consteval & constinit

  • consteval- 指定一个函数是一个立即函数,即每次调用该函数都必须产生一个编译时常量s
constexpr unsigned factorial(unsigned n) {
    return n < 2 ? 1 : n * factorial(n - 1);
}

consteval unsigned combination(unsigned m, unsigned n) {
    return factorial(n) / factorial(m) / factorial(n - m);
}
 
static_assert(factorial(6) == 720);
static_assert(combination(4,8) == 70);
 
int main(int argc, const char*[]) {
 
    constexpr unsigned x{factorial(4)};
    std::cout << x << '\n';
 
    [[maybe_unused]]
    unsigned y = factorial(argc); // OK
//  unsigned z = combination(argc, 7); // error: 'argc' 不是一个常量表达式
}
  • constinit- 断言一个变量有静态初始化,即零初始化和常量初始化,否则程序是病态的
const char *g() { return "dynamic initialization"; }
constexpr const char *f(bool p) { return p ? "constant initializer" : g(); }
 
constinit const char *c = f(true); // OK
// constinit const char *d = f(false); // error

Coroutines

  • 协程是一个可以暂停执行以便稍后恢复的函数。协程是无堆栈的:它们通过返回给调用者来暂停执行,并且恢复执行所需的数据与堆栈分开存储。这允许异步执行的顺序代码。目前C++20并没有封装好的类库,使用还是比较繁琐,可以等C++23看看
#include 
#include 
//编译环境 vs2017
struct generator
{
    struct promise_type;
    using handle = std::experimental::coroutine_handle<promise_type>;
    struct promise_type
    {
        static auto get_return_object_on_allocation_failure() { return generator(nullptr); }
        auto get_return_object() { return generator{ handle::from_promise(*this) }; }
        auto initial_suspend() { return std::experimental::suspend_always{}; }
        auto final_suspend() { return std::experimental::suspend_always{}; }
        void unhandled_exception() { std::terminate(); }
        void return_void() {}
        //auto return_value(int value) { return value; }
    };
    bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
    int current_value() { return coro.promise().current_value; }
    generator(generator const &) = delete;
    generator(generator &&rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
    ~generator() { if (coro) coro.destroy(); }

private:
    generator(handle h) : coro(h) {}
    handle coro;
};

generator f()
{
    std::cout << "hello";
    co_await std::experimental::suspend_always();
    std::cout << "world";
}

int main()
{
    auto ret = f();
    while (ret.move_next())
    {
        std::cout << " ";
    }// hello world
    getchar();
    return 0;
}

Modules

  • 模块是一种跨翻译单元共享声明和定义的语言功能。它们是一些头文件用例的替代方案
//  A.cpp   
export module A;
 
export import :B;
import :C;    
 
export char const* World() {
    return WorldImpl();
}

// A-B.cpp 
export module A:B;
export char const* Hello() { return "Hello"; }

// A-C.cpp 
module A:C;
char const* WorldImpl() { return "World"; }

// main.cpp 
import A;
import ;
 
int main() 
{
    std::cout << Hello() << ' ' << World() << '\n';
}

Concepts

  • 类模板、函数模板和非模板函数(通常是类模板的成员)可能与一个约束相关联,该约束指定了对模板参数的要求,可用于选择最合适的函数重载和模板特化,更多详情查看
template<typename T>
concept Addable = requires (T obj) {{obj + obj}->std::same_as<T>;};//约束a+b的类型必须和T类型一致
T add(T a, T b)
{
	return a + b;
}

新增标准库函数

std::format

  • 替代sprintf
#include 
#include 
int main() {
    std::string message = std::format("The answer is {}.", 42);//The answer is 42.
    
    std::string buffer;
    std::format_to(std::back_inserter(buffer), "Hello, C++{}!\n", "20");//向后插入,Hello, C++20!

	auto buff = "{0}:{1}:{2}";
	auto h = 17;
	auto m = 46;
	auto s = 55;
	auto size = std::format_size(buff, h, m, s);
	std::string ti;
	ti.resize(size);
	std::format_to_n(const_cast<char*>(ti.c_str()), ti.size(), buff, h, m, s);
	ti.push_back('\0');//17:46:55
}

std::chrono

  • 日历时间库
#include 
#include 
 
long fibonacci(unsigned n)
{
    if (n < 2) return n;
    return fibonacci(n-1) + fibonacci(n-2);
}
 
int main()
{
    auto start = std::chrono::steady_clock::now();
    std::cout << "f(42) = " << fibonacci(42) << '\n';
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
    //elapsed time: 1.88232s
}

std::source_location

  • 代码信息,可用于日志
#include 
#include 
#include 
 
void log(const std::string_view message,
         const std::source_location location = 
               std::source_location::current())
{
    std::cout << "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!");//file: main.cpp(24:8) `int main(int, char**)`: Hello world!
    fun("Hello C++20!");//file: main.cpp(19:8) `void fun(T) [with T = const char*]`: Hello C++20!
}

std::span

  • 数组视图,类似于std::string_view
#include 
#include 
 
template<class T, std::size_t N> [[nodiscard]]
constexpr auto slide(std::span<T,N> s, std::size_t offset, std::size_t width) 
{
    return s.subspan(offset, offset + width <= s.size() ? width : 0U);
}

void print(const auto& seq) 
{
    for (const auto& elem : seq) std::cout << elem << ' ';
    std::cout << '\n';
}
 
int main()
{
    constexpr int a[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
 
    for (std::size_t offset{}; ; ++offset) 
    {
        static constexpr std::size_t width{6};
        auto s = slide(std::span{a}, offset, width);
        if (s.empty())
            break;
        print(s);
    }
}

std::endian

  • 判断当前环境是大端序还是小端序
#include 
#include 
 
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::make_shared

  • 支持数组构造
std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
std::shared_ptr<std::vector<int>[4]> spC = std::make_shared<std::vector<int>[4]>({5, 6});

std::remove_cvref

  • 移除const,varient,引用属性
#include 
#include 
 
int main()
{
    std::cout << std::boolalpha
              << std::is_same_v<std::remove_cvref_t<int>, int> << '\n'
              << std::is_same_v<std::remove_cvref_t<int&>, int> << '\n'
              << std::is_same_v<std::remove_cvref_t<int&&>, int> << '\n'
              << std::is_same_v<std::remove_cvref_t<const int&>, int> << '\n'
              << std::is_same_v<std::remove_cvref_t<const int[2]>, int[2]> << '\n'
              << std::is_same_v<std::remove_cvref_t<const int(&)[2]>, int[2]> << '\n'
              << std::is_same_v<std::remove_cvref_t<int(int)>, int(int)> << '\n';
}
/*
true
true
true
true
true
true
true
*/

std::to_address

  • 获取原始指针
#include 
#include 

int main()
{
    std::shared_ptr<int> a(new int{0});
    std::cout << a.get() << "  " << std::to_address(a);
	//0x493eb0  0x493eb0
    return 0;
}

std::ssize & std::size

  • 获取数组最大size
#include 
#include 
#include 
 
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) can avail
    auto i = std::ssize(v);
    std::cout << "i = " << i << '\n';
    for (--i; i != -1; --i) {
        std::cout << v[i] << (i ? ' ' : '\n');
    }
    std::cout << "i = " << i << '\n';
}
/*
3
3
i = 3
4 1 3
i = -1
*/

std::latch

  • 该类latch是std::ptrdiff_t类型的向下计数器,可用于同步线程。计数器的值在创建时初始化。线程可能会阻塞在锁存器上,直到计数器减为零。不可能增加或重置计数器,这使得锁存器成为一次性屏障。
#include 
#include 
#include 
#include 
#include 
 
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();//join等待线程执行完毕
  }
  std::cout << "done:\n";
  for (auto const& job : jobs) 
  {
    std::cout << "  " << job.product << '\n';
  }
}
/*
Work starting... done:
  annika worked
  buru worked
  chuck worked
Workers cleaning up... done:
  annika cleaned
  buru cleaned
  chuck cleaned
*/

std::barrier

-类模板std::barrier提供了一种线程协调机制,该机制最多允许预期数量的线程阻塞,直到预期数量的线程到达屏障。与std::latch不同,屏障是可重用的:一旦到达的线程从屏障阶段的同步点解除阻塞,就可以重用相同的屏障

#include 
#include 
#include 
#include 
#include 
 
int main() 
{
  const auto workers = { "anil", "busara", "carl" };
 
  auto on_completion = []() noexcept 
  { 
    // locking not needed here
    static auto phase = "... done\nCleaning up...\n";
    std::cout << phase;
    phase = "... done\n";// 第一遍执行完后phase就改变了值
  };
  std::barrier sync_point(std::ssize(workers), on_completion);
 
  auto work = [&](std::string name) 
  {
    std::string product = "  " + name + " worked\n";
    std::cout << product;  // 3个线程先执行到这儿,然后执行一遍on_completion 
    sync_point.arrive_and_wait();
 
    product = "  " + name + " cleaned\n";
    std::cout << product;// 然后到这儿,再执行一遍on_completion 
    sync_point.arrive_and_wait();// 将此句删除就不会再执行第二遍的on_completion
  };
 
  std::cout << "Starting...\n";
  std::vector<std::thread> threads;
  for (auto const& worker : workers) 
  {
    threads.emplace_back(work, worker);
  }
  for (auto& thread : threads) 
  {
    thread.join();
  }
}
/*
Starting...
  anil worked
  busara worked
  carl worked
... done
Cleaning up...
  carl cleaned
  busara cleaned
  anil cleaned
... done
*/

std::counting_semaphore & std::binary_semaphore

  • std::counting_semaphore 简单的说即根据信号量(内部计数器)大于和等于零进行不同的操作,acquire 会让信号量减一,release 会让信号量加一
  • 大于零,进行 acquire 操作,信号量减一,不会堵塞线程等于零,进行 acquire 操作,信号量为零,无法再减,堵塞线程,直到 release 操作让信号量加一(大于零),重复上一步
  • std::binary_semaphore 是std::counting_semaphore<1>的别名
#include 
#include 
#include 
#include 
 
std::binary_semaphore
	smphSignalMainToThread{0},
	smphSignalThreadToMain{0};
 
void ThreadProc()
{	
	smphSignalMainToThread.acquire();//得到 -1
 
	std::cout << "[thread] Got the signal\n"; 

	using namespace std::literals;
	std::this_thread::sleep_for(std::chrono::seconds(3));
 
	std::cout << "[thread] Send the signal\n";

	smphSignalThreadToMain.release();
}
 
int main()
{
	std::thread thrWorker(ThreadProc);
 
	std::cout << "[main] Send the signal\n";
 
	smphSignalMainToThread.release();//+1
 
	smphSignalThreadToMain.acquire();
 
	std::cout << "[main] Got the signal\n";
	thrWorker.join();
}
/*
[main] Send the signal
[thread] Got the signal
[thread] Send the signal
[main] Got the signal
*/
  • std::counting_semaphore
#include 
#include 
#include 
#include 

std::counting_semaphore<3>
        smphSignalMainToThread{0},
        smphSignalThreadToMain{0};

void ThreadProc1()
{
    smphSignalMainToThread.acquire();

    std::cout << "[thread] Got the signal1\n";

    using namespace std::literals;
    std::this_thread::sleep_for(std::chrono::seconds(3));

    std::cout << "[thread] Send the signal1\n";

    smphSignalThreadToMain.release();
}

void ThreadProc2()
{
    smphSignalMainToThread.acquire();

    std::cout << "[thread] Got the signal2\n";

    using namespace std::literals;
    std::this_thread::sleep_for(std::chrono::seconds(3));

    std::cout << "[thread] Send the signal2\n";

    smphSignalThreadToMain.release();
}

void ThreadProc3()
{
    smphSignalMainToThread.acquire();

    std::cout << "[thread] Got the signal3\n";

    using namespace std::literals;
    std::this_thread::sleep_for(std::chrono::seconds(3));

    std::cout << "[thread] Send the signal3\n";

    smphSignalThreadToMain.release();
}

int main()
{
    std::thread thrWorker1(ThreadProc1);
    std::thread thrWorker2(ThreadProc2);
    std::thread thrWorker3(ThreadProc3);

    std::cout << "[main] Send the signal\n"; 

    smphSignalMainToThread.release(3);

    smphSignalThreadToMain.acquire();

    std::cout << "[main] Got the signal\n";
    thrWorker1.join();
    thrWorker2.join();
    thrWorker3.join();
}
/*
[main] Send the signal
[thread] Got the signal1
[thread] Got the signal2
[thread] Got the signal3
[thread] Send the signal3
[thread] Send the signal2
[thread] Send the signal1
[main] Got the signal
*/

std::jthread

  • 修复std::thread的不足,加入外部主动停止线程
  • std::stop_token 类提供是否已经或能对其所关联的 std::stop_source 对象作出停止请求的方法。它实质上是关联停止状态的线程安全视图
  • stop_source 类提供发出停止请求的方式
  • stop_callback 类模板提供对关联的 std::stop_token 对象注册回调函数的 RAII 对象类型,使得将在 std::stop_token 的关联 std::stop_source 被请求停止时调用回调函数
#include 
#include 
#include 
#include 

int main()
{
    auto fn = [&](std::stop_token st)
    {
        while(!st.stop_requested())
        {
            std::cout << 1 << "\t";
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    };
    std::jthread td(fn);
    std::this_thread::sleep_for(std::chrono::seconds(3));
    td.request_stop();//如果没有这句话在main函数结束时析构td也会自动调用停止
    return 0;
}

std::basic_osyncstream

  • std::basic_osyncstream 是 std::basic_syncbuf 的便利包装。它提供机制以同步写入同一流的线程
#include 
#include 
int main()
{
#if 1
    std::osyncstream sync_out(std::cout); // std::cout 的同步包装
    sync_out << "Hello, ";
    sync_out << "World!";
    sync_out << std::endl; // 注意有冲入,但仍未进行
    sync_out << "and more!\n";
#else
	std::osyncstream(std::cout) << "Hello, " << "World!" << '\n';
#endif	    
    return 0;
} // 转移字符并冲入 std::cout 
/*
Hello, World!
and more!
*/

std::starts_with & std::ends_with

  • std::starts_wit若 string 始于前缀起始则为 true ,否则为 false
#include 
#include 
#include 
 
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("world"));
 
    test_prefix_print(helloWorld, 'h');
 
    test_prefix_print(helloWorld, 'd');
}
/*
'hello world' starts with 'hello': true
'hello world' starts with 'world': false
'hello world' starts with 'h': true
'hello world' starts with 'd': false
*/
  • std::ends_with 若 string 终于给定后缀则为 true ,否则为 false
    上方starts_with换成ends_with,输出结果为:
    'hello world' starts with 'hello': false
    'hello world' starts with 'world': true
    'hello world' starts with 'h': false
    'hello world' starts with 'd': true
  • std::string_view
#include 
#include 
 
auto main() -> int
{
    using namespace std::literals;
 
    std::cout
        << std::boolalpha

        << "https://cppreference.com"sv.starts_with("http"sv) << ' ' // true
        << "https://cppreference.com"sv.starts_with("ftp"sv) << ' '  // false
 
        << "C++20"sv.starts_with('C') << ' ' // true
        << "C++20"sv.starts_with('J') << ' ' // false
 
        << std::string_view("string_view").starts_with("string") << ' ' // true
        << std::string_view("string_view").starts_with("String") << ' ' // false
        << '\n';
}

std::assume_aligned

  • 告知编译器 ptr 所指向的对象至少对齐到 N 。实现可用此信息生成更高效的代码
void f(int* p) {
   int* p1 = std::assume_aligned<256>(p);
   // 用 p1 而非 p ,以确保从对齐假设受益。
   // 然而,若 p 未对齐则程序有未定义行为,无关乎是否使用 p1 。
}

std::bind_front

  • 有意令此函数取代 std::bind,不同于 std::bind,它不支持任意参数重排,而且不特别处理嵌套的 bind 表达式或 std::reference_wrapper。另一方面,它注重调用包装对象的值类别,并传播底层调用运算符的异常规定
#include 
#include 
 
int minus(int a, int b){
    return a-b;
}
 
struct S
{
  int val;
  int minus(int arg) const noexcept { return val - arg; }
};
 
int main()
{
    auto fifty_minus = std::bind_front(minus, 50);
    std::cout << fifty_minus (3) << '\n';
 
    auto member_minus = std::bind_front(&S::minus, S{50});
 
 
    std::cout << member_minus (3) << '\n';
 
    // 保持 noexcept 说明!
    static_assert(! noexcept(fifty_minus (3)));
    static_assert(noexcept(member_minus (3)));
 
    // 绑定 lambda
    auto plus = [](int a, int b) { return a+b; };
    auto forty_plus = std::bind_front(plus, 40);
    std::cout << forty_plus(7) << '\n';
}

std::midpoint

  • 计算整数、浮点或指针 a 与 b 的中点,可以简单地实现为 return a + (b - a) / 2
#include 
#include 
#include 
#include 
 
int main()
{
    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);
    return 0;
}
/*
std::midpoint('2', '4'): '3'
std::midpoint('2', '5'): '3'
std::midpoint('5', '2'): '4'
std::midpoint('2', '6'): '4'
*/

std::lerp

  • 计算 a+t(b−a) ,即 a 与 b 间参数为 t 的线性内插(或为外插,当 t 落在范围 [0,1] 外时)
#include 
#include 
 
int main()
{
    float a=10.0f, b=20.0f;
 
    std::cout << "a=" << a << ", " << "b=" << b << '\n'
              << "mid point=" << std::lerp(a,b,0.5f) << '\n'
              << std::boolalpha << (a == std::lerp(a,b,0.0f)) << ' '
              << std::boolalpha << (b == std::lerp(a,b,1.0f)) << '\n';
    return 0;
}
/*
a=10, b=20
mid point=15
true true
*/

std::is_bounded_array & std:: is_unbounded_array

  • std::is_bounded_array检查 T 是否为拥有已知边界的数组类型。若 T 是有已知边界的数组则提供等于 true 的成员常量 value 。否则 value 等于 false
  • std:: is_unbounded_array 正好相反
#include 
#include 
 
class A {};
 
int main() 
{
    std::cout << std::boolalpha;
    std::cout << std::is_bounded_array_v<A> << '\n'; // false
    std::cout << std::is_bounded_array_v<A[]> << '\n'; // false
    std::cout << std::is_bounded_array_v<A[3]> << '\n'; // true
    std::cout << std::is_bounded_array_v<float> << '\n'; // false
    std::cout << std::is_bounded_array_v<int> << '\n'; // false
    std::cout << std::is_bounded_array_v<int[]> << '\n'; // false
    std::cout << std::is_bounded_array_v<int[3]> << '\n'; // true

	std::cout << std::boolalpha;
    std::cout << std::is_unbounded_array_v<A> << '\n'; // false
    std::cout << std::is_unbounded_array_v<A[]> << '\n'; // true
    std::cout << std::is_unbounded_array_v<A[3]> << '\n'; // false
    std::cout << std::is_unbounded_array_v<float> << '\n'; // false
    std::cout << std::is_unbounded_array_v<int> << '\n'; // false
    std::cout << std::is_unbounded_array_v<int[]> << '\n'; // true
    std::cout << std::is_unbounded_array_v<int[3]> << '\n'; // false
    return 0;
}

std::ranges

  • 范围(ranges):是“项目集合”或“可迭代事物”的抽象。最基本的定义只需要存在begin()和end()在范围内。Range 代表一串元素, 或者一串元素中的一段类似 begin/end 对
  • 更多详情请查看:std::ranges
#include 
#include 
 
int main()
{
    auto const ints = {0,1,2,3,4,5};
    auto even = [](int i) { return 0 == i % 2; };
    auto square = [](int i) { return i * i; };
 
    // 组合视图的“管道”语法:
    for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
        std::cout << i << ' ';
    }
 
    std::cout << '\n';
 
    // 传统的“函数式”组合语法:
    for (int i : std::views::transform(std::views::filter(ints, even), square)) {
        std::cout << i << ' ';
    }
} // 0 4 16 

std::erase & std::erase_if

  • 从容器中擦除所有比较等于 value 的元素,从容器中擦除所有满足 pred 的元素
#include 
#include 
#include 
 
void print_container(const std::vector<char>& c)
{
    for (auto x : c) {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}
 
int main()
{
    std::vector<char> cnt(10);
    std::iota(cnt.begin(), cnt.end(), '0');
 
    std::cout << "Init:\n";
    print_container(cnt);// 0 1 2 3 4 5 6 7 8 9 
 
    auto erased = std::erase(cnt, '3');
    std::cout << "Erase \'3\':\n";
    print_container(cnt);// 0 1 2 4 5 6 7 8 9 
 
    std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });
    std::cout << "Erase all even numbers:\n";
    print_container(cnt);// 1 5 7 9
    std::cout << "In all " << erased << " even numbers were erased.\n";// 5
}

std::numbers

  • 提供数个数学常数,详情请查看:std::numbers
#include 
#include 
#include 
#include 
#include 
#include 
 
int main()
{
    using namespace std::numbers;
 
    std::cout
        << std::pow(e, ln2) / 2 << ' '
        << std::pow(std::cosh(pi), 2) - std::pow(std::sinh(pi), 2) << ' '
        << std::sqrt(pi) * inv_sqrtpi << ' '
        << std::pow(sqrt2 * sqrt3, 2) / 6 << ' '
        << sqrt3 * inv_sqrt3 << ' '
        << log2e * ln2 << ' '
        << log10e * ln10 << ' '
        << pi * inv_pi << ' '
        << phi * phi - phi << '\n';
 
    auto egamma_aprox = [] {
        long double s = 0, m = 2.0;
        for (unsigned c = 2; c != 1'000'000; ++c, ++m) {
            const long double t = std::riemann_zeta(m) / m;
            (c & 1) == 0 ? s += t : s -= t;
        }
        return s;
    };
 
    std::cout << std::fixed << (egamma_v<long double> - egamma_aprox()) << '\n';
 
    constexpr std::string_view γ {"0.577215664901532860606512090082402"};
 
    std::cout 
        << "γ as egamma_v       = "
        << std::setprecision(std::numeric_limits<float>::digits10 + 1)
        << egamma_v<float> << '\n'
        << "γ as egamma_v      = "
        << std::setprecision(std::numeric_limits<double>::digits10 + 1)
        << egamma_v<double> << '\n'
        << "γ as egamma_v = "
        << std::setprecision(std::numeric_limits<long double>::digits10 + 1)
        << egamma_v<long double> << '\n'
        << "γ with " << γ.length() - 1 << " digits precision = " << γ << '\n';
    return 0;
}
/*
1 1 1 1 1 1 1 1 1
0.000001
γ as egamma_v       = 0.5772157
γ as egamma_v      = 0.5772156649015329
γ as egamma_v = 0.5772156649015328606
γ with 34 digits precision = 0.577215664901532860606512090082402
*/

你可能感兴趣的:(C++新标准,visual,studio,c++)