导出命名空间
export module name;
export namespace name {
int foo(int fir, int sec);
void bar();
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
简单示例
模块文件 simple.ixx
export module simple;
export int add(int a, int b) { return a + b; }
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
export module simple;
是一个模块声明, 它声明了一个模块, 模块名为simple
.
在模块中, 我们可以使用export
关键字导出函数, 命名空间, 类, 变量等.
这样的实体可被其他模块导入.
主程序 simple_main.cpp
在使用的时候, 使用import simple;
导入模块.
import simple;
#include
int main() {
std::cout << "simple add(1, 2)=" << add(1, 2) << "\n";
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
CMakeList.txt
add_library(simple_module)
target_sources(simple_module
PUBLIC
FILE_SET CXX_MODULES FILES
simple.ixx
)
add_executable(simple_demo simple_main.cpp)
target_link_libraries(simple_demo simple_module)
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
复杂示例
模块文件
请注意如果模块需要引入传统头文件, 需要使用如下写法.
module;
#include
#include
export module complex;
export namespace complex {
class Person {
public:
Person(unsigned age, std::string name) : age_(age), name_(std::move(name)) {}
[[nodiscard]] std::string Name() const { return name_; }
[[nodiscard]] unsigned Age() const { return age_; }
private:
unsigned age_;
std::string name_;
};
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
程序主文件
import complex;
#include
int main() {
complex::Person joy(18, "Joy");
std::cout << "Name: " << joy.Name() << "\n";
std::cout << "Age : " << joy.Age() << "\n";
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
cmake 设置
add_library(complex_module)
target_sources(complex_module
PUBLIC
FILE_SET CXX_MODULES FILES
complex.ixx
)
add_executable(complex_demo complex_main.cpp)
target_link_libraries(complex_demo complex_module)
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
模块接口
当模块变得越来越大时, 可能需要把模块的接口和实现分开. 这样可以更好地组织代码, 并且提高代码的可读性.
模块接口单元
module;
#include
export module demo;
export namespace demo {
int sum(const std::vector<int>& v);
int prod(const std::vector<int>& v);
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
模块实现单元
module demo;
#include
#include
namespace demo {
int sum(const std::vector<int>& v) {
return std::accumulate(v.begin(), v.end(), 1, std::plus{});
}
int prod(const std::vector<int>& v) {
return std::accumulate(v.begin(), v.end(), 1, std::multiplies{});
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
主程序
import demo;
#include
#include
int main() {
std::vector vec{1, 2, 3, 4, 5};
std::cout << "sum : " << demo::sum(vec) << std::endl;
std::cout << "prod : " << demo::prod(vec) << std::endl;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
cmake 设置如下:
add_library(separate_module)
target_sources(separate_module
PUBLIC
FILE_SET CXX_MODULES FILES
separate_interface.ixx
)
target_sources(separate_module PUBLIC separate_impl.ixx)
add_executable(separate separate_main.cpp)
target_link_libraries(separate separate_module)
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
子模块
对于大的模块可以将其分割为多个子模块. 然后设置一个主模块文件, 导入并导出子模块.
这里的每一个子模块都是一个独立的模块, 可以单独存在.
模块主文件 sort.ixx
设想一下我们目前有一个排序算法库, 包含几种不同的排序算法, 每种排序算法都可以独立使用.
module;
export module sort;
export import sort.bubble_sort;
export import sort.insert;
export import sort.quick;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
子模块文件
module;
#include
#include
export module sort.bubble_sort;
export namespace sort {
void bubble_sort(std::vector<int>& arr) {
if (arr.size() < 2) return;
for (int i = 0; i < arr.size() - 1; i++) {
auto swapped = false;
for (int j = 1; j < arr.size() - i; j++) {
if (arr[j - 1] > arr[j]) {
std::swap(arr[j - 1], arr[j]);
swapped = true;
}
}
if (!swapped) break;
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
module;
#include
export module sort.insert;
export namespace sort {
void insertion_sort(std::vector<int>& arr) {
for (int i = 1; i < arr.size(); i++) {
auto k = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > k) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = k;
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
程序主文件
import sort;
#include
#include
#include
int main() {
std::vector v{5, 4, 3, 2, 1};
sort::bubble_sort(v);
std::cout << std::boolalpha << std::is_sorted(v.begin(), v.end(), std::less{})
<< std::endl;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
cmake 设置
add_library(sort_module)
target_sources(sort_module
PUBLIC
FILE_SET CXX_MODULES FILES
sort.ixx
sort_bubble.ixx
sort_quick.ixx
sort_insert.ixx
)
add_executable(sort_module_demo sort_main.cpp)
target_link_libraries(sort_module_demo sort_module)
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
模块分区
模块分区与子模块类似, 唯一的区别是分区模块不能作为一个独立模块存在.
模块主文件
module;
export module partition;
export import :part1;
export import :part2;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
子分区文件
module;
#include
#include
export module partition:part1;
export namespace sort {
void bubble_sort(std::vector<int>& arr) {
if (arr.size() < 2) return;
for (int i = 0; i < arr.size() - 1; i++) {
auto swapped = false;
for (int j = 1; j < arr.size() - i; j++) {
if (arr[j - 1] > arr[j]) {
std::swap(arr[j - 1], arr[j]);
swapped = true;
}
}
if (!swapped) break;
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
module;
#include
export module partition:part2;
export namespace sort {
void insertion_sort(std::vector<int>& arr) {
for (int i = 1; i < arr.size(); i++) {
auto k = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > k) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = k;
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
cmake 设置
add_library(partition_module)
target_sources(partition_module
PUBLIC
FILE_SET CXX_MODULES FILES
partition.ixx
partition_part1.ixx
partition_part2.ixx
)
add_executable(partition_module_demo partition_main.cpp)
target_link_libraries(partition_module_demo partition_module)
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
总结
-
模块是 C++20 的新特性, 它可以解决头文件的低效问题. 导入模块的代价非常低, 并且导入顺序不重要.
模块还可以解决名称冲突问题.
-
模块由模块接口单元和模块实现单元组成. 必须有一个导出模块声明的模块接口单元, 以及任意多个模块实现单元.
在模块接口中没有导出的名称具有模块链接, 不能在模块外部使用.
-
模块可以有头文件, 也可以导入和重新导出其它模块.
-
C++20 标准库没有模块化. 使用 C++20 构建模块化的软件系统是一项具有挑战性的任务.
-
为了构建大型软件系统, 模块提供了两种方法: 子模块和模块分区. 与分区不同, 子模块可以独立存在.
-
由于头文件单元的存在, 可以用导入语句替换包含语句, 编译器会自动生成一个模块.
参考链接
评论记录:
回复评论: