C++ 標準并沒有特別提到子模塊,但允許在模塊名稱中使用點(.),從而可以按任何你想要的層次結構來組織模塊。例如,以下是一個 DataModel 命名空間的示例:
export module datamodel;import <vector>;export namespace DataModel { class Person { /* ... */ }; class Address { /* ... */ }; using Persons = std::vector<Person>;}
Person 和 Address 類都在 DataModel 命名空間內,也在 datamodel 模塊中。可以通過定義兩個子模塊來重構:datamodel.person 和 datamodel.address。
datamodel.person 子模塊的模塊接口文件如下:
export module datamodel.person; // datamodel.person 子模塊export namespace DataModel { class Person { /* ... */ };}
datamodel.address 子模塊的模塊接口文件如下:
export module datamodel.address; // datamodel.address 子模塊export namespace DataModel { class Address { /* ... */ };}
最后,定義一個 datamodel 模塊如下。它導入并立即導出兩個子模塊。
export module datamodel; // datamodel 模塊export import datamodel.person; // 導入并導出 person 子模塊export import datamodel.address; // 導入并導出 address 子模塊import <vector>;export namespace DataModel { using Persons = std::vector<Person>;}
當然,子模塊中類的方法實現也可以放在模塊實現文件中。例如,假設 Address 類有一個默認構造函數,僅打印一條語句到標準輸出;該實現可以放在一個名為 datamodel.address.cpp 的文件中:
module datamodel.address; // datamodel.address 子模塊import <iostream>;using namespace std;DataModel::Address::Address() { cout << "Address::Address()" << endl;}
用子模塊結構化代碼的好處是,客戶端可以導入他們想要使用的特定部分,或者一次性導入所有內容。例如,如果客戶端僅對使用 Address 類感興趣,以下導入聲明就足夠了:
import datamodel.address;
另一方面,如果客戶端代碼需要訪問 datamodel 模塊中的所有內容,那么以下導入聲明是最簡單的:
import datamodel;
分區和子模塊之間的區別在于,子模塊結構對模塊的使用者是可見的,允許用戶選擇性地只導入他們想使用的子模塊。另一方面,分區用于內部結構化模塊,對模塊的使用者不可見。在模塊接口分區文件中聲明的所有分區最終必須由主要的模塊接口文件導出。一個模塊始終只有一個這樣的主模塊接口文件,即包含 export module 名稱聲明的接口文件。
模塊分區是通過將模塊名稱和分區名稱用冒號分隔來創建的。分區名稱可以是任何合法的標識符。例如,前一節中的 DataModel 模塊可以使用分區而不是子模塊來重構。以下是 datamodel.person.cppm 模塊接口分區文件中的 person 分區:
export module datamodel:person; // datamodel:person 分區export namespace DataModel { class Person { /* ... */ };}
使用分區時的一個注意事項是:與分區相結合的實現文件只能有一個文件具有特定的分區名稱。因此,以下聲明開始的實現文件是不正確的:
module datamodel:address;
相反,你可以將 address 分區的實現放在 datamodel 模塊的實現文件中:
module datamodel; // 不是 datamodel:address!import <iostream>;using namespace std;DataModel::Address::Address() { cout << "Address::Address()" << endl;}
警告:多個文件不能有相同的分區名稱。因此,擁有多個具有相同分區名稱的模塊接口分區文件是非法的,且分區文件中聲明的實現不能放在具有相同分區名稱的實現文件中。相反,應該將這些實現放在模塊的實現文件中。
編寫分區結構的模塊時,要記住的重要一點是,每個模塊接口分區最終必須由主模塊接口文件直接或間接導出。要導入分區,只需指定分區名稱,前綴為冒號,例如 import :person。說 import datamodel:person 是非法的。請記住,分區對模塊的使用者不可見;分區只在模塊內部結構化。因此,用戶不能導入特定的分區;他們必須導入整個模塊。分區只能在模塊內部導入,因此在冒號前指定模塊名稱是多余的(且非法的)。
以下是 datamodel 模塊的主模塊接口文件:
export module datamodel; // datamodel 模塊(主模塊接口文件)export import :person; // 導入并導出 person 分區export import :address; // 導入并導出 address 分區import <vector>;export namespace DataModel { using Persons = std::vector<Person>;}
import datamodel;int main() { DataModel::Address a;}
注意:分區用于內部結構化模塊。分區在模塊外部不可見。因此,模塊的用戶不能導入特定分區;他們必須導入整個模塊。早先提到,模塊名稱聲明隱含地包含一個導入名稱聲明。但對于分區,情況并非如此。例如,datamodel:person 分區沒有隱含的 import datamodel 聲明。在這個例子中,甚至不允許在 datamodel:person 接口分區文件中添加顯式的 import datamodel 聲明。這樣做會導致循環依賴:datamodel 接口文件包含 import :person 聲明,而 datamodel:person 接口分區文件會包含 import datamodel 聲明。
實現分區不需要在模塊接口分區文件中聲明,它也可以在模塊實現分區文件中聲明,這是一個帶有 .cpp 擴展名的普通源代碼文件,在這種情況下,它是一個實現分區,有時也稱為內部分區。這種分區不會被主模塊接口文件導出。例如,假設你有以下數學主模塊接口文件(math.cppm):
export module math; // math 模塊聲明export namespace Math { double superLog(double z, double b); double lerchZeta(double lambda, double alpha, double s);}
假設進一步數學函數的實現需要一些不能被模塊導出的輔助函數。實現分區是放置這些輔助函數的完美位置。以下在名為 math_helpers.cpp 的文件中定義了這樣的實現分區:
module math:details; // math:details 實現分區double someHelperFunction(double a) { return /* ... */;}
其他數學模塊實現文件可以通過導入這個實現分區來訪問這些輔助函數。例如,一個數學模塊實現文件(math.cpp)可能看起來像這樣:
module math;import :details;double Math::superLog(double z, double b) { return /* ... */;}double Math::lerchZeta(double lambda, double alpha, double s) { return /* ... */;}
當然,使用帶有輔助函數的這種實現分區只有在多個其他源文件使用這些輔助函數時才有意義。
本文鏈接:http://www.www897cc.com/showinfo-26-55017-0.htmlC++ module編程升級指南,子模塊與分區全解析
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com