c++20新增的特性非常多,其中concept,coroutine,module和range为四大特性,在之前的章节中已有涉及,本文则对其他的一些小改动进行讲解。
std::map::contains
c++20支持新的字符串格式化方式std::format
#include
#include
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;
}
#include
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();
}
时区工具
#include
#include
int main() {
const std::chrono::zoned_time cur_time{ std::chrono::current_zone(),
std::chrono::system_clock::now() };
std::cout << cur_time << '\n';
}
文件位置的工具
#include
#include
#include
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!");
}
过去如果一个函数想接受无法确定数组长度的数组作为参数,那么一定需要声明两个参数:数组指针和长度:
void set_data(int *arr, int len) {}
int main()
{
int buf[128]{ 0 };
set_data(buf, 128);
}
这种人工输入增加了编码的风险,数组长度的错误输入会引发程序的未定义行为,甚至是成为可被利用的漏洞。C++20标准库为我们提供了一个很好解决方案std::span,通过它可以定义一个基于连续序列对象的视图,包括原生数组,并且保留连续序列对象的大小。例如:
#include
#include
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
#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 if (res == 0)
std::cout << "-0 and 0 are equal";
else
std::cout << "-0 and 0 are unordered";
}
#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";
}
去除cv和引用
#include
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)>);
}
和std::bind是一个系列的方法,bind_front可以绑定前n个参数而不用敲placeholder。
#include
#include
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);
}
原子引用
在下面的例子中,最终将打印100
#include
#include
#include
#include
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
#include
#include
#include
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
#include
#include
#include
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::contains
map新增contains方法,在此之前使用的是find或者count。
#include
#include
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";
}
}
}
线程屏障
#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\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);
}
latch = single-use barrier.
#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();
}
std::cout << "done:\n";
for (auto const& job : jobs) {
std::cout << " " << job.product << '\n';
}
}
计数信号量
#include
#include
#include
#include
// global binary semaphore instances
// object counts are set to zero
// objects are in non-signaled state
std::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();
}
字符串开头/结尾
#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("goodbye"));
test_prefix_print(helloWorld, 'h');
test_prefix_print(helloWorld, 'x');
}
size的common方法
#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) is available
auto i = std::ssize(v);
for (--i; i != -1; --i)
std::cout << v[i] << (i ? ' ' : '\n');
std::cout << "i = " << i << '\n';
}
#include
#include
#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
#include
#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]> );
}
按照条件erase数据
#include
#include
#include
#include
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
#include
#include
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;
}
c++20新增了一些数学常量
#include
#include
int main() {
std::cout << std::numbers::log2e_v<double> << std::endl;
}
#include
#include
#include
#include
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);
}
线性计算
a+t(b-a)
#include
#include
#include
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';
}