C++之父都說(shuō)過(guò),C++20是C++語(yǔ)言的一次重大變革,引入了大量的新特性。
這其中個(gè)人認(rèn)為最重要新特性是這三個(gè):
Modules
Coroutines
Concepts
Modules
直接看代碼:
// helloworld.ixx
export module helloworld; // module declaration
import <iostream>; // import declaration
export void hello() { // export declaration
std::cout << "Hello world!\n";
}
// main.cpp\n import helloworld; // import declaration
int main() {
hello();
}
每個(gè)C++開(kāi)發(fā)者應(yīng)該都知道include方式是將header中的代碼拷貝一份到源文件中,在大的工程項(xiàng)目中還有很多冗余的include,種種原因,導(dǎo)致編譯速度相當(dāng)?shù)穆鴐odules卻大大改善了這種問(wèn)題。
modules使用方式和include差不多,但modules使用比include頭文件速度更快,C++全球開(kāi)發(fā)者大會(huì)中,C++之父貼出來(lái)過(guò)測(cè)試數(shù)據(jù),modules效率比include高了25倍。
以后modules肯定會(huì)是主流使用方式。
Coroutines
協(xié)程是一種比線程和進(jìn)程更高效的多任務(wù)處理模型。
在C++20中,終于把協(xié)程引了進(jìn)來(lái),協(xié)程具體還分為有棧協(xié)程和無(wú)棧協(xié)程,兩者對(duì)比,無(wú)棧協(xié)程是更高效的協(xié)程。
而C++20支持的就是無(wú)棧協(xié)程,為此提供了三個(gè)關(guān)鍵字:
co_await:暫停執(zhí)行,直到恢復(fù)
co_yield:暫停執(zhí)行,返回一個(gè)值
co_return:完成執(zhí)行,返回一個(gè)值
這是一段cppreference上,協(xié)程相關(guān)的示例代碼:
auto switch_to_new_thread(std::jthread& out) {
struct awaitable {
std::jthread* p_out;
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<> h) {
std::jthread& out = *p_out;
if (out.joinable())
throw std::runtime_error("Output jthread parameter not empty");
out = std::jthread([h] { h.resume(); });
// Potential undefined behavior: accessing potentially destroyed *this
// std::cout << "New thread ID: " << p_out->get_id() << '\n';
std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK
}
void await_resume() {}
};
return awaitable{&out};
}
struct task{
struct promise_type {
task get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
task resuming_on_new_thread(std::jthread& out) {
std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';
co_await switch_to_new_thread(out);
// awaiter destroyed here
std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';
}
int main() {
std::jthread out;
resuming_on_new_thread(out);
}
目前C++20只是從語(yǔ)法層面對(duì)協(xié)程做了支持,還沒(méi)有相關(guān)的協(xié)程庫(kù),要想使用協(xié)程,還需要引入?yún)f(xié)程庫(kù),但不可否認(rèn),C++20已經(jīng)支持了協(xié)程,以后在這方面肯定會(huì)越來(lái)越完善。
前一段時(shí)間參加的C++全球開(kāi)發(fā)者大會(huì),C++之父也說(shuō)過(guò)會(huì)全力在C++23標(biāo)準(zhǔn)中引入對(duì)協(xié)程庫(kù)的支持。
Concepts
Concepts在模板編程中起到重大的作用, 類模板、函數(shù)模板和非模板函數(shù)(通常是類模板的成員)可以與一個(gè)約束相關(guān)聯(lián),這個(gè)約束指定了對(duì)模板實(shí)參的要求,這些實(shí)參可用于選擇最合適的函數(shù)重載和模板特化。
很多人應(yīng)該都知道SFINAE,在C++20前多數(shù)都在使用std::enable_if,這相當(dāng)?shù)穆闊a可讀性也不高,編譯器報(bào)錯(cuò)信息也不是很友好,而有了Concepts就方便的多, 每個(gè)Concepts都是一個(gè)謂詞,在編譯時(shí)計(jì)算,并成為模板接口的一部分,在那里它被用作約束:
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
struct meow {};
// Constrained C++20 function template:
template<Hashable T>
void f(T) {}
int main() {
using std::operator""s;
f("abc"s); // OK, std::string satisfies Hashable
//f(meow{}); // Error: meow does not satisfy Hashable
}
其實(shí)C++20還有一些特性我比較喜歡,比如std::jthread,真真切切的解決了std::thread存在的舊有問(wèn)題。
還有fmt,因?yàn)?strong>cout方式輸出復(fù)雜的格式很麻煩:比如我想輸出a + b = c:
std::cout << a << " + " << b << " = " << c << std::endl;
是不是很麻煩,那可以使用printf?
printf("%d + %d = %d \n", a, b, c);
但printf需要開(kāi)發(fā)者填入變量格式,如果填寫(xiě)有誤,有可能打印錯(cuò)誤,也有可能crash:
某個(gè)long long 型,使用%d打印會(huì)怎么樣?
某個(gè)int32_t型,使用%lld打印會(huì)怎么樣?
某個(gè)const char* 型,使用%d打印會(huì)怎么樣?
某個(gè)float型,使用%d打印會(huì)怎么樣?
所以使用printf會(huì)給開(kāi)發(fā)者帶來(lái)負(fù)擔(dān),一定要在打印前確認(rèn)變量的類型,并且一定要保證配置的輸出格式正確,我想大多數(shù)人可能都會(huì)在print某個(gè)變量前搜索過(guò)打印某個(gè)類型需要使用哪種格式吧?
那用啥呢?cout配合format。
std::cout << std::format("{} + {} = {} \n", a, b, c);
C++20其實(shí)還有很多有意思的新特性,而這些特性在這本書(shū)中都有詳細(xì)的介紹,大家感興趣可以看看。