今天向大家介紹c++20核心語法modules,從c++11到今天的c++20,這是c++不斷推陳出新從量變到質變的過程。在介紹modules之前,我們先來了解include機制。
include機制
c++為了完全兼容c語言,讓c語言開發者快速學習c++語言,因此繼承了c語言的include機制。include是包含的意思,就是將頭文件的所有內容複製過來,#則代表預處理的意思,相關的預處理還有#ifdef、#endif,在此就不做過多的介紹。
c++將代碼分為頭文件和cpp源文件,頭文件主要是申明函數等相關接口,cpp文件則是具體實現。開發者可以將cpp文件編譯成lib文件,使用者無需關係其具體的實現,這樣能很好的將實現邏輯和接口分離。
什麼是編譯單元
當一個c或cpp文件在編譯時,預處理器首先遞歸包含頭文件,形成一個含有所有必要信息的單個源文件,這個源文件就是一個編譯單元。
編譯單元和頭文件
在同一個編譯單元裡,重複包含頭文件會照成重複定義的問題,例如:一個頭文件定義了一個class(不是申明),然後在一個cpp文件中包含兩次,編譯器就會報“類型重定義”錯誤。為了解決這一編譯錯誤問題,必須要在頭文件裡寫上#ifndef、#define 和 #endif(新版編譯器你可以#pragma once),但大多數人認為這一解決辦法並不完美。
編譯真的很慢很慢
c++編譯慢是公認的,你會發現編譯一個工程的時間足夠幹很多事情,其中有一大部分原因就是include照成的。一個公共的頭文件很可能被N個編譯單元包含,這N個編譯單元都會去編譯頭文件,尤其是修改這個公共頭文件,再編譯簡直讓人慾哭無淚,準確的詮釋了“牽一髮而動全身”。
可以看出include的機制有一系列問題,這些問題總是不盡人意,曾經有多少人抱怨include機制太爛,又有多少人為此轉移陣營而投靠其它語言。面對標準委員會的一次又一次推移,最終module特性凍結在c++20。
modules重磅來襲
C++不適應在大規模程序設計與現代開發中的應用環境,越來越多的模板的使用,已經導致了編譯時可伸縮性和程序員生產力的嚴重障礙。它構建性能低下,與雲和分佈式構建的集成性差。此外,嚴重依賴頭文件包含(即從編譯器的角度複製代碼)和宏扼殺了C++開發人員的開發。
為了解決以上系列問題,c++20 modules應運而生!(手動特效:金光閃閃)
其中,WG21官方的n4465.pdf文檔對modules的目標描述:
1. componentization;
2. isolation from macros;
3. scalable build;
4. support for modern semantics-aware developer tools.
gcc官方對其描述:
Reduce build times due to not reparsing large header files
Proper interface/implementation isolation
Harder to have ODR violations
mvc、gcc、clang 三大編譯器基本已完成c++17的開發工作,目前正在開發c++20核心功能。gcc編譯器很早就建立了module分支,可以從gcc官網看到,2017年3月1日 第一個模塊可執行文件運行。mvc已經開始試驗性的支持module,可以從微軟官方c++博客看到,vs2015就開始支持modules了,我相信正式版本將很快與我們見面。
開始modules工作
如果您使用的是Visual Studio 2017 15.3之前的版本,請添加/experimental:module /module:stdIfcDir "$(VCToolsInstallDir_150)ifc\\$(PlatformTarget)"到`` 配置屬性''-> `` C / C ++''->``命令行''以打開該項目的模塊。
(我的版本是vs2017 15.9.7 ,所以我直接跳過上一步)接下來,項目屬性頁->c/c++->語言->c++語言標準選擇“最新草案標準(/std:c++latest)”,啟用c++模塊(實驗性)選擇“是”。
//#include <vector>
import std.core;
int main()
{
\tstd::vectorvec; /<vector>
\tvec.push_back(1);
\tvec.push_back(2);
\tvec.push_back(3);
\tfor (auto it = vec.begin(); it != vec.end(); ++it)
\t{
\t\tprintf("%d\\n", *it);
\t}
\tsystem("pause");
\treturn 0;
}
以上是stl的modules示例,微軟官方提供的std模塊參考如下
- std.regex 提供標題的內容 <regex>
- std.filesystem 提供標題的內容 <experimental>
- std.memory 提供標題的內容 <memory>
- std.threadingprovodes頭部的內容<atomic>,<condition>,<future>,<mutex>,<shared>,<thread>/<shared>/<mutex>/<future>/<condition>/<atomic>
- std.core 提供C ++標準庫中的所有其他內容
自定義模塊
新建Mo.ixx文件(注意後綴名的變化),然後我們需要在main函數前導入MO模塊(import MO;)並使用它,需要注意的是隻有標記export的才會被導出,否則外部無法調用。我們可以在模塊中導入子模塊,可以用大括號批量export,也可以更小粒度的控制export。
export module MO;
import std.core;
export
{
\tvoid test()
\t{
\t\tprintf("test。。。");
\t}
\tnamespace SPACE
\t{
\t\tclass File
\t\t{
\t\tpublic
\t\t\tvoid process();
\t\t};
\t\t
\t}
}
void test2()
{
\tprintf("test2。。。");
}
void SPACE::File::process()
{
}
編譯它會生成兩個文件,分別是obj和ifc。obj我們並不陌生,那麼ifc是個什麼東西?其實它是模塊的二進制描述文件,無法手動更改,雖然沒有了頭文件,但是需要一個ifc文件,讓編譯器知道接口信息,用於和其它TU建立橋樑。可以看到模塊直接被當做TU處理,無論你有多少個編譯單元import該模塊,都不會重複編譯,大大提高了編譯速度,是不是很酷!
c++20 module 將是一個全新的開始,所帶來改變是巨大的,這些改變是不言而喻的,從這一刻開始構建你的大型項目。好吧,我們扔掉include,從modules開始,更快的構建速度,更清爽的代碼架構,更小的功能依賴,更好的獨立組件,像c#一樣高效而優雅的開發。
結束語
modules終於來了!模塊對於開發的好處毋庸置疑,我相信這是很多人都有所期待的,那些抱怨include垃圾而轉移陣營的同學,是時候迴歸c++了。
從c++11到c++20,還在用c++98的你,我就問你慌不慌?當下急速發展的互聯網時代,不學習就意味著淘汰,希望大家勇於接受新事物!
對此有任何疑問,歡迎大家留言,謝謝!
閱讀更多 cpp架構 的文章