Skip to content
Go back

C++20 Modules

· Updated:

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指定しない
    • 内部リンケージの名前が外部に見えてしまうことを防ぐため、らしい

参考文献