目录

一、什么是modules

二、vs版本要求

三、项目配置

四、标准库模块引入方式

五、兼容旧式写法

六、导出类与变量

七、文件后缀

八、实现与主接口分开

九、模块命名与分区

十、引入模块

十一、模块分区

十一、模块私有部分

十二、结语


大家好,我是略游。今天讲一讲我在vs上操作C++20新标准模块(modules)的经验。此文章具有时效性,只代表当前实验结果,也可作为一个基本写法入门,将来的编译器一定会完善得更好。

一、什么是modules

modules是C++20标准的源码组织方式,以替代#include等传统的源码组织方式。与它相关的基本知识网上有很多文章,废话不多说直接实际操作。

二、vs版本要求

官方文档说16.10版本以上,更新最新版本即可。使用vs2022也可,不过需要注意的是需要安装以下工具:

虽然在随后的实验中,我没有成功使用上标准库的模块接口,但这个工具是否必须安装,我暂时懒得测试了,在这里获取工具:

三、项目配置

首先设置C++语言标准为/std:c++ latest,并且启用标准库模块

在定义使用最新特性的命令行/await

在链接器中关闭使用增量链接,同时使用程序数据库,这二者是配套的,如果不关闭增量链接,会与模块编译有冲突,后者不能正常识别修改。

四、标准库模块引入方式

按照微软文档的说法,如下即可导入标准库模块:

import std.core;
import std.filesystem;
//...

但实际操作中很困难,会提示一些编译选项冲突。除了/EHsc和/MD选项,还有一些其他的定义冲突,例如_DEBUG宏。

文档链接如下:

Overview of modules in C++ | Microsoft Docs

五、兼容旧式写法

要以#include包含旧式头文件,可以如下写:

module;
#include "Head.h"
export module Test;

这里包含了一个头文件Head.h,然后导出了一个模块叫Test。但是此处有一些局限性,Head.h不能是预编译头,预编译头是msvc的一种加快编译速度的方式,但是这里是不能兼容的。

在Head.h里面我包含了一大串头文件,这样我们也能使用上标准库。当前这样的写法,缺点是每次修改都需要重新编译所包含的头文件内容。预编译头可以解决这个问题,等完全使用上模块后,也能解决这个问题。现在只是兼容的写法。

//Head.h
#pragma once
#include <array>      //!< 数组
#include <list>           //!< 链表
//more...

值得一提的是,我在包含windows头文件时,遇到一些报错无法解决,最后只好不直接包含windows头文件了,暂时使用传统方法隐藏起来。

语法很简明,在“module;”和“export module Test;”之间就是旧式的包含头文件方式。

六、导出类与变量

前面加export即可导出类与全局变量,如下:

module;
#include "Head.h"
export module Test;export class Test
{//...
};export Test g_test;

这里就不用再像以往一样,害怕变量重定义,不能直接放置到头文件。在以前的写法就是这样的:

//--------------Test.h
#include "Head.h"class Test
{//...
};extern Test g_test;//--------------Test.cpp
Test g_test;

另外constexpr常量并不能直接导出,因为它是不变量,在编译期就会替换为数值,并不会真实存在这个变量,只能通过命名空间间接导出。

//-----------failed
export constexpr size_t DEFINE_NUM_GOODS_00 = 16 * 22;//-----------ok
export
namespace Define
{constexpr size_t NUM_GOODS_00 = 16 * 22;
}

七、文件后缀

C++标准没有规定文件的后缀名必须是什么,但是到目前为止,msvc必须使用.ixx后缀名,否则编译器会报错。

同时还应该设置文件项类型为C/C++编译器 ,如下图所示:

为了区分主接口文件,我们可以让实现文件后缀为.cpp,但仍然注意属性页里的项类型也要是C/C++编译器。

直接右键添加一个模块文件,这样也是正确的:

八、实现与主接口分开

我们可以直接在主接口文件实现Test::Init成员函数的定义,但也可以分开文件存放。例如以下文件Test.cpp,在兼容旧写法的同时,还需表明自己是Test模块的一部分。

module;
#include "Head.h"
module Test;void Test::Init()
{
}

在以前的写法,将函数定义在头文件的严重缺点就是,修改此函数就会导致整个文件被修改,然后所有包含此文件的源码都需要重新编译。而模板函数必须放在头文件,不但编译速度极慢,而且无法规避这个问题。而使用模块后便可以改进这个缺点,同时我们还能保持文件分开以保证代码的整洁性。

九、模块命名与分区

在一些示例中我们可以看到用点来分隔名字,比如export module Test.A,但实际上Test.A就是一个模块的名字,它是一个整体,并不代表Test模块的A分区(点在这里没有任何意义,只是为了好看)。正规的分区写法如下:

//Test.A是一个独立的模块
export module Test.A;//A是Test的分区
export module Test:A;

另外微软推荐文件名命名与模块名相似,假设一个模块叫Test.Point:Draw,那么它的文件名应该是Test.Point-Draw.ixx,实现文件可以叫Test.Point-Draw.cpp。即用-来代替:。

十、引入模块

很简单,如下写即可:

import Test;

如果想要“转发”模块,在自己引入X的时候,同时引入自己的地方也会引入X,如下所示:

export module Test;export import Test.Image;
export import Test.Sprite;
export import Test.Tex;
export import Test.Text;
export import Test.UI;

当另一个文件引入Test时,就不需要再引入Test.Image等了,只需import Test就会import Test.Image等。注意这里的.只是名字的一部分,而不是模块分区,只是为了好看。

如果只是Test自己使用,去掉export则不会转发:

export module Test;import Test.Image;
import Test.Sprite;
import Test.Tex;
import Test.Text;
import Test.UI;

十一、模块分区

前面用“.”来分隔名字只是一种权宜手段,它们本质上是单独的模块,其余文件不需要通过Test来导入Test.Image,可以直接导入Test.Image。而分区模块如果主接口没有导出,则其他文件是使用不了的。

假设有个Test:Struct分区,在Main.cpp想要导入。那么这么写是无效的:

import Test:Struct; //error

而用.的话,就可以:

import Test.Image; //ok

所以用分区的区别在于此,并且从定义上来说Test:Image是真正属于Test的。

要定义一个模块分区,如下:

export module Test:Struct;export
struct A
{//...
};

在上面的代码,在Test:Struct分区导出了一个类A。在Test主接口文件,同样可以选择import或者export import。这决定了Test:Struct是否对外界可见。

//导出模块Test
export module Test;//导入Define
import Define;//导入Test.Image并且使导入Test的自动导入Test.Image
export import Test.Image;//导入Test.Ui供自己使用
import Test.UI;//导入分区,但是不导出,也可以前面加export导出
import :Struct;

注意import模块分区时,则必须省略冒号前面的模块名, 想必编译器也是由此判断的。因为不属于Test的模块会import Test:Struct,然而这是不允许的。而属于Test的模块,是知道自己在Test的,所以可以省略“:”前的Test。

十一、模块私有部分

在声明module :private之后的内容只对自己文件可见,当然这是对于实现文件模块分区来说的。因为一个类或变量是否导出,取决于前面是否有export关键字。

module :private;

十二、结语

如果你觉得此文章有帮到你,可以点击收藏,然后点击关注,这可以极大的支持我发更多的文章。

你还可以加我的QQ群讨论:游戏编程星云阁 170100866

【C++20】vs2019使用modules的实际操作相关推荐

  1. Linux常用命令(本篇包括,Linux目录结构介绍、Linux Shell介绍、9个常见命令介绍、文件的概念、文件的操作(20个)、目录的操作、文件和目录的权限、文件压缩及解压缩)

    Linux常用命令(本篇包括,Linux目录结构介绍.Linux Shell介绍.9个常见命令介绍.文件的概念.文件的操作(20个).目录的操作.文件和目录的权限.文件压缩及解压缩)         ...

  2. 【华为软件特战队2023.2.20笔试题】 最小缩进操作次数

    [软件特战队2023.2.20笔试题] 最小缩进操作次数 题目描述 解题思路 伪代码 完整代码 结尾 朋友分享给我的,简单记录下 题目描述 请实现一个简单的代码缩进功能,把一段未缩进的代码,通过多次操 ...

  3. 20 个短小精悍的 pandas 骚操作

    本次为大家整理了一个pandas骚操作操作的大集合,共20个功能,个个短小精悍,一次让你爱个够. 1. ExcelWriter 很多时候dataframe里面有中文,如果直接输出到csv里,中文将显示 ...

  4. 必知之vs2019添加外加库文件操作

    温馨提示 添加的目录如果放在与main.cpp文件的相同目录下,可以使用相对目录即可,"./xxx目录" 配置结束如果没有立即生效,可以重新打开vs2019测试 1.添加工程的头文 ...

  5. C++20 要来了!

    导读:C++的新标准又双叒叕要到来了,是的,C++20要来了! 本文经授权转自公众号CSDN(ID:CSDNnews),作者:祁宇 ▲图片来源:udemy.com 几周前,C++标准委会历史上规模最大 ...

  6. K8Sv1.20二进制多master部署

    二进制搭建 Kubernetes v1.20 k8s集群master01:192.168.80.80 kube-apiserver kube-controller-manager kube-sched ...

  7. linux文件编辑操作,Linux下文本编辑及其文件操作

    文本编辑及其文件操作 Vim 命令模式: dd 删除当前行 yy 2yy nyy 复制 从光标算起,复制n行 p 粘贴 默认粘贴在光标的下一行 u 撤销 末行模式: :wq 保存退出 :wq! 强制保 ...

  8. vue 怎么全局到入常量_午后躺椅上看关于Vue的20点

    1.var,const,let的区别 首先,必须明确let 与const是ES6 中为了修复var的缺陷引入的,那么var有什么缺陷呢? var 1.var没有块级作用域 在JS函数中的var声明,其 ...

  9. python文件对象提供了3个读方法、分别是-python3基础之文件对象操作

    1.向文本文件中写入内容 s = 'Hello world 文本文件的读取方法 文本文件的写入方法 ' # 需要写入文件的字符串 print('显示需要写入的内容: {0:s}'.format(s)) ...

最新文章

  1. Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)解决方法
  2. AutoMapper 9.0的改造
  3. mbp网速很慢_苹果笔记本上网很慢怎么回事?macbook无线上网慢的解决方法
  4. java 泛型 类型形参(Type Parameters)Type Parameters 边界(Bound)
  5. springboot项目启动成功后执行一段代码的两种方式
  6. Linux和Windows下使用指定的JDK运行jar包
  7. 致运维——运维军团告诉你如何走过七年之痒
  8. SQL索引及表的页的逻辑顺序与物理顺序
  9. 此页的状态信息无效,可能已损坏。”的解决办法
  10. 给Activity设置背景色
  11. PAIP.如何选择安全的即时通讯IM工具.txt
  12. visio 模板_Mac软件推荐:免费的流程图软件,完美替代Visio
  13. 数据库之SQL行列转换
  14. 旧手机改服务器,并配合花生壳实现外网访问的方法
  15. 运放输入偏置电流方向_输入偏置电流和输入失调电流(运放参数的详细解释和分析)...
  16. linux ubuntu 五笔输入法,ubuntu下安装fcitx五笔输入法
  17. cannot unbox null value
  18. 《Learning to Reconstruct Botanical Trees from Single Images》学习从单幅图像重建植物树
  19. python爬取斗鱼主播图片_F_hawk189_新浪博客
  20. kubernetes 【组件】ingress 如何通过域名访问您的应用

热门文章

  1. 鸿蒙系统真实评测,鸿蒙系统和安卓的区别-华为鸿蒙系统使用体验评测
  2. 六十六、Leetcode数组系列(中篇)
  3. EnvironmentError: [WinError 5] 拒绝访问
  4. 四、CSS知识总结(下篇)
  5. 如何将因果干预用于提升模型公平性?
  6. 直播 | ACL 2021论文解读:低资源语言场景下的跨语言文本摘要
  7. 畅享云游戏,AWS云峰会邀你零距离体验强化学习!
  8. 基于多域连接卷积神经网络的精神分裂症脑功能网络分类
  9. gitclone 一个tag的地址_一个无锁队列和FreeList实现
  10. 记录一些使用git过程中的bug