语言特性
__has_cpp_attribute
- 检查是否存在由属性标记命名的属性,例如deprecated等
#include <iostream>
#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 <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
std::cout << "-0 and 0 are equal";
}
聚合初始化
struct test
{
int a;
int b;
};
int main()
{
test t{ .b = 10, .a = 20 };
test t{ .a = 10, .b = 20 };
return 0;
}
char8_t
[[no_unique_address]]
- 适用于在不是位字段的非静态数据成员的声明中声明的名称,编译器可以优化它以不占用空间
#include <iostream>
struct Empty {};
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);
}
- constinit- 断言一个变量有静态初始化,即零初始化和常量初始化,否则程序是病态的
const char *g() { return "dynamic initialization"; }
constexpr const char *f(bool p) { return p ? "constant initializer" : g(); }
constinit const char *c = f(true);
Coroutines
- 协程是一个可以暂停执行以便稍后恢复的函数。协程是无堆栈的:它们通过返回给调用者来暂停执行,并且恢复执行所需的数据与堆栈分开存储。这允许异步执行的顺序代码。目前C++20并没有封装好的类库,使用还是比较繁琐,可以等C++23看看
#include <iostream>
#include <experimental/coroutine>
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() {}
};
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 << " ";
}
getchar();
return 0;
}
Modules
- 模块是一种跨翻译单元共享声明和定义的语言功能。它们是一些头文件用例的替代方案
export module A;
export import :B;
import :C;
export char const* World() {
return WorldImpl();
}
export module A:B;
export char const* Hello() { return "Hello"; }
module A:C;
char const* WorldImpl() { return "World"; }
import A;
import <iostream>;
int main()
{
std::cout << Hello() << ' ' << World() << '\n';
}
Concepts
- 类模板、函数模板和非模板函数(通常是类模板的成员)可能与一个约束相关联,该约束指定了对模板参数的要求,可用于选择最合适的函数重载和模板特化,更多详情查看
template<typename T>
concept Addable = requires (T obj) {{obj + obj}->std::same_as<T>;};
T add(T a, T b)
{
return a + b;
}
新增标准库函数
std::format
#include <format>
#include <cassert>
int main() {
std::string message = std::format("The answer is {}.", 42);
std::string buffer;
std::format_to(std::back_inserter(buffer), "Hello, C++{}!\n", "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');
}
std::chrono
#include <iostream>
#include <chrono>
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";
}
std::source_location
#include <iostream>
#include <string_view>
#include <source_location>
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!");
fun("Hello C++20!");
}
std::span
#include <iostream>
#include <span>
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 <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::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
#include <iostream>
#include <type_traits>
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';
}
std::to_address
#include <memory>
#include <iostream>
int main()
{
std::shared_ptr<int> a(new int{0});
std::cout << a.get() << " " << std::to_address(a);
return 0;
}
std::ssize & std::size
#include <iostream>
#include <vector>
#include <iterator>
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';
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';
}
std::latch
- 该类latch是std::ptrdiff_t类型的向下计数器,可用于同步线程。计数器的值在创建时初始化。线程可能会阻塞在锁存器上,直到计数器减为零。不可能增加或重置计数器,这使得锁存器成为一次性屏障。
#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::barrier
-类模板std::barrier提供了一种线程协调机制,该机制最多允许预期数量的线程阻塞,直到预期数量的线程到达屏障。与std::latch不同,屏障是可重用的:一旦到达的线程从屏障阶段的同步点解除阻塞,就可以重用相同的屏障
#include <barrier>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
int main()
{
const auto workers = { "anil", "busara", "carl" };
auto on_completion = []() noexcept
{
static auto phase = "... done\nCleaning 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;
sync_point.arrive_and_wait();
product = " " + name + " cleaned\n";
std::cout << product;
sync_point.arrive_and_wait();
};
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();
}
}
std::counting_semaphore & std::binary_semaphore
- std::counting_semaphore 简单的说即根据信号量(内部计数器)大于和等于零进行不同的操作,acquire 会让信号量减一,release 会让信号量加一
- 大于零,进行 acquire 操作,信号量减一,不会堵塞线程等于零,进行 acquire 操作,信号量为零,无法再减,堵塞线程,直到 release 操作让信号量加一(大于零),重复上一步
- std::binary_semaphore 是std::counting_semaphore<1>的别名
#include <iostream>
#include <thread>
#include <chrono>
#include <semaphore>
std::binary_semaphore
smphSignalMainToThread{0},
smphSignalThreadToMain{0};
void ThreadProc()
{
smphSignalMainToThread.acquire();
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();
smphSignalThreadToMain.acquire();
std::cout << "[main] Got the signal\n";
thrWorker.join();
}
#include <iostream>
#include <thread>
#include <chrono>
#include <semaphore>
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();
}
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 <iostream>
#include <thread>
#include <chrono>
#include <semaphore>
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();
return 0;
}
std::basic_osyncstream
- std::basic_osyncstream 是 std::basic_syncbuf 的便利包装。它提供机制以同步写入同一流的线程
#include <iostream>
#include <syncstream>
int main()
{
#if 1
std::osyncstream sync_out(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::starts_with & std::ends_with
- std::starts_wit若 string 始于前缀起始则为 true ,否则为 false
#include <iostream>
#include <string_view>
#include <string>
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');
}
- 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 <iostream>
#include <string_view>
auto main() -> int
{
using namespace std::literals;
std::cout
<< std::boolalpha
<< "https://cppreference.com"sv.starts_with("http"sv) << ' '
<< "https://cppreference.com"sv.starts_with("ftp"sv) << ' '
<< "C++20"sv.starts_with('C') << ' '
<< "C++20"sv.starts_with('J') << ' '
<< std::string_view("string_view").starts_with("string") << ' '
<< std::string_view("string_view").starts_with("String") << ' '
<< '\n';
}
std::assume_aligned
- 告知编译器 ptr 所指向的对象至少对齐到 N 。实现可用此信息生成更高效的代码
void f(int* p) {
int* p1 = std::assume_aligned<256>(p);
}
std::bind_front
- 有意令此函数取代 std::bind,不同于 std::bind,它不支持任意参数重排,而且不特别处理嵌套的 bind 表达式或 std::reference_wrapper。另一方面,它注重调用包装对象的值类别,并传播底层调用运算符的异常规定
#include <functional>
#include <iostream>
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';
static_assert(! noexcept(fifty_minus (3)));
static_assert(noexcept(member_minus (3)));
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 <cstdint>
#include <limits>
#include <numeric>
#include <iostream>
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::lerp
- 计算 a+t(b?a) ,即 a 与 b 间参数为 t 的线性内插(或为外插,当 t 落在范围 [0,1] 外时)
#include <iostream>
#include <cmath>
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;
}
std::is_bounded_array & std:: is_unbounded_array
- std::is_bounded_array检查 T 是否为拥有已知边界的数组类型。若 T 是有已知边界的数组则提供等于 true 的成员常量 value 。否则 value 等于 false
- std:: is_unbounded_array 正好相反
#include <iostream>
#include <type_traits>
class A {};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_bounded_array_v<A> << '\n';
std::cout << std::is_bounded_array_v<A[]> << '\n';
std::cout << std::is_bounded_array_v<A[3]> << '\n';
std::cout << std::is_bounded_array_v<float> << '\n';
std::cout << std::is_bounded_array_v<int> << '\n';
std::cout << std::is_bounded_array_v<int[]> << '\n';
std::cout << std::is_bounded_array_v<int[3]> << '\n';
std::cout << std::boolalpha;
std::cout << std::is_unbounded_array_v<A> << '\n';
std::cout << std::is_unbounded_array_v<A[]> << '\n';
std::cout << std::is_unbounded_array_v<A[3]> << '\n';
std::cout << std::is_unbounded_array_v<float> << '\n';
std::cout << std::is_unbounded_array_v<int> << '\n';
std::cout << std::is_unbounded_array_v<int[]> << '\n';
std::cout << std::is_unbounded_array_v<int[3]> << '\n';
return 0;
}
std::ranges
- 范围(ranges):是“项目集合”或“可迭代事物”的抽象。最基本的定义只需要存在begin()和end()在范围内。Range 代表一串元素, 或者一串元素中的一段类似 begin/end 对
- 更多详情请查看:std::ranges
#include <ranges>
#include <iostream>
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 << ' ';
}
}
std::erase & std::erase_if
- 从容器中擦除所有比较等于 value 的元素,从容器中擦除所有满足 pred 的元素
#include <iostream>
#include <numeric>
#include <vector>
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);
auto erased = std::erase(cnt, '3');
std::cout << "Erase \'3\':\n";
print_container(cnt);
std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });
std::cout << "Erase all even numbers:\n";
print_container(cnt);
std::cout << "In all " << erased << " even numbers were erased.\n";
}
std::numbers
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <numbers>
#include <string_view>
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<float> = "
<< std::setprecision(std::numeric_limits<float>::digits10 + 1)
<< egamma_v<float> << '\n'
<< "γ as egamma_v<double> = "
<< std::setprecision(std::numeric_limits<double>::digits10 + 1)
<< egamma_v<double> << '\n'
<< "γ as egamma_v<long double> = "
<< std::setprecision(std::numeric_limits<long double>::digits10 + 1)
<< egamma_v<long double> << '\n'
<< "γ with " << γ.length() - 1 << " digits precision = " << γ << '\n';
return 0;
}
|