C++20のモジュールについて個人的にまとめたものです。内容には間違いが含まれている場合があります。
Module units
- module宣言のある翻訳単位をmodule unitと呼ぶ
- ヘッダーファイル相当のmodule interface unitとソースファイル相当のmodule implementation unitの2つがある
- さらに、モジュールを複数のmodule partition unitに分割して構成することもできる
- 同じモジュールのmodule unitの集合をnamed moduleと呼ぶ
Module interface units
export module my_module;
// importにも付けられる
export import module1;
// 宣言や定義に付けられる
export void foo();
export void bar() {}
// ブロックにも付けられる
export {
void block();
}
// 名前空間にも付けられる
export namespace ns {
void ns_block();
}module宣言で翻訳単位をmodule unitにする:export module my_module;と宣言すると、その翻訳単位はmy_moduleという名前のモジュールのprimary module interface unitになるexport module my_module:my_partition;と宣言すると、その翻訳単位はmy_moduleという名前のモジュールにあるmy_partitionという名前のパーティションのmodule interface unitになる
import宣言で他所から宣言と定義を取り込む:import module_name;と宣言すると、module_nameという名前のモジュールのprimary module interface unitでエクスポートされた宣言と定義を自身に取り込むimport :partition_name;と宣言すると、自身と同じモジュールにあるpartition_nameという名前のパーティションのmodule unitにあるすべての宣言と定義を自身に取り込む
exportを付けて、モジュールの外から見えるようにする:importに付けると、そこでエクスポートされている宣言と定義が自身のモジュールでさらにエクスポートされる
Module implementation units
module my_module;
import module2;
void foo() {}module宣言で翻訳単位をmodule unitにする:module my_module;と宣言すると、その翻訳単位はmy_moduleという名前のモジュールのmodule implementation unitになるmodule my_module:my_partition;と宣言すると、その翻訳単位はmy_moduleという名前のモジュールにあるmy_partitionという名前のパーティションのmodule implementation unitになる
import宣言で他所から宣言と定義を取り込む: -import module_name;と宣言すると、module_nameという名前のモジュールのprimary module interface unitでエクスポートされた宣言と定義を自身に取り込むimport :partition_name;と宣言すると、自身と同じモジュールにあるpartition_nameという名前のパーティションのmodule unitにあるすべての宣言と定義を自身に取り込む
- module implementation unitでは
exportを付けられない
Header units
import <iostream>;
import "my_header.hpp";- header unitはヘッダーファイルから個別に生成されるmodule unitのこと
- コンパイル済みヘッダーのようなもの
- すべての宣言と定義がエクスポートされている
Fragments
Global module fragment
module;
// --- ここからglobal module fragment ---
#define MY_MACRO
#include "my_header.hpp"
// --- ここまでglobal module fragment ---
module my_module;- module unitはその先頭に
module;を書くことができ、通常のmodule宣言までの間にプリプロセッサ・ディレクティブを記述できる- この区間をglobal module fragmentと呼ぶ
Private module fragment
export module my_primary_module_interface;
...
module : private
// --- ここからprivate module fragment
// この翻訳単位しか見えない関数
void private_function();- primary module interface unitはその終端に
module : private;を書くことができ、それ以降の宣言と定義はその翻訳単位でしか見えなくなる- この区間をprivate module fragmentと呼ぶ
注意点
- モジュール名はドット区切りの識別子列で記述する
- ドット自体に特別な意味はなく、表記上で階層を表すのに使える
- モジュール名やパーティション名はプリプロセッサで置換できない
- パーティション名はそのモジュール内で重複不可
- primary module interface unitはモジュールに1つだけ
- import宣言はmodule宣言とその他の宣言の間に記述しなければならない
- inline関数は定義がただひとつ、かつ、使う側から到達可能でなければならない
- つまり、宣言にinlineをつけていれば、定義をmodule implementation unitに書いても良いが、ヘッダーの時と同様に実装の詳細を隠すことはできない
- モジュールでは、クラス内で定義されるメンバー関数を暗黙的にinline指定しない
- 内部リンケージの名前が外部に見えてしまうことを防ぐため、らしい
参考文献
- https://onihusube.hatenablog.com/entry/2019/07/07/025446
- https://onihusube.hatenablog.com/entry/2019/07/17/183137
- https://onihusube.hatenablog.com/entry/2021/04/30/230638#%E3%83%A1%E3%83%B3%E3%83%90%E9%96%A2%E6%95%B0%E3%81%AE%E6%9A%97%E9%BB%99inline
- https://vector-of-bool.github.io/2019/10/07/modules-3.html
- https://en.cppreference.com/w/cpp/language/modules.html