はじめに
C++20で導入されたモジュールシステムは、ヘッダーファイルの問題を解決する新しい仕組み。
インクルードガードとかプリプロセッサの闇から解放されるよ。
なぜモジュールが必要か
従来のヘッダーファイルの問題:
- プリプロセッサによるテキスト置換
- インクルードガードが必要
- ビルド時間の増大
- マクロの漏洩
- ODR(One Definition Rule)違反のリスク
モジュールの基本
モジュールの作成
// math.ixx (モジュールインターフェースユニット)
export module math;
export int add(int a, int b) {
return a + b;
}
export int multiply(int a, int b) {
return a * b;
}
// 内部実装(エクスポートしない)
int internal_helper(int x) {
return x * 2;
}
モジュールの使用
// main.cpp
import math;
#include <iostream>
int main() {
std::cout << add(3, 4) << std::endl; // 7
std::cout << multiply(5, 6) << std::endl; // 30
// internal_helper(10); // エラー!エクスポートされていない
return 0;
}
モジュールパーティション
大きなモジュールを分割できます:
// math-add.ixx
export module math:add;
export int add(int a, int b) {
return a + b;
}
// math-multiply.ixx
export module math:multiply;
export int multiply(int a, int b) {
return a * b;
}
// math.ixx (プライマリモジュールインターフェース)
export module math;
export import :add;
export import :multiply;
モジュール実装ユニット
インターフェースと実装を分離:
// math.ixx (インターフェース)
export module math;
export int add(int a, int b);
export int multiply(int a, int b);
// math.cpp (実装)
module math;
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
グローバルモジュールフラグメント
従来のヘッダーを使う場合:
module;
// グローバルモジュールフラグメント
#include <iostream>
#include <vector>
#include <string>
export module mymodule;
export void greet(const std::string& name) {
std::cout << "Hello, " << name << "!" << std::endl;
}
標準ライブラリモジュール (C++23)
// C++23
import std; // 標準ライブラリ全体
int main() {
std::cout << "Hello, Modules!" << std::endl;
std::vector<int> v = {1, 2, 3};
return 0;
}
// または
import std.compat; // C互換ヘッダーも含む
モジュールのビルド
MSVC
cl /std:c++20 /experimental:module /c math.ixx
cl /std:c++20 /experimental:module main.cpp math.obj
GCC
g++ -std=c++20 -fmodules-ts -c math.ixx
g++ -std=c++20 -fmodules-ts main.cpp math.o
Clang
clang++ -std=c++20 -fmodules --precompile math.ixx -o math.pcm
clang++ -std=c++20 -fmodules -fprebuilt-module-path=. main.cpp math.pcm
CMake (3.28+)
cmake_minimum_required(VERSION 3.28)
project(ModuleDemo CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(math)
target_sources(math
PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES
math.ixx
)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE math)
モジュールの利点
| 従来のヘッダー | モジュール |
|---|---|
| #include でテキスト挿入 | import でバイナリ読み込み |
| 複数回パース | 一度だけコンパイル |
| マクロが漏洩 | マクロは漏洩しない |
| インクルード順序依存 | 順序非依存 |
| ODR違反のリスク | 安全 |
ビルド時間の改善
従来のヘッダー: プリプロセス → パース → コード生成 (毎回)
モジュール: プリコンパイル済みモジュール読み込み (高速)
移行戦略
- 段階的移行: 新規コードからモジュール化
- ヘッダーユニット: 既存ヘッダーをモジュールのように使う
- import : ヘッダーをインポート形式で使用
// ヘッダーユニット
import <vector>;
import <string>;
import <iostream>;
現状の対応状況
| コンパイラ | 対応状況 |
|---|---|
| MSVC 19.28+ | ほぼ完全対応 |
| GCC 11+ | 実験的対応(-fmodules-ts) |
| Clang 16+ | 対応進行中 |
注意点
- ビルドシステムの対応が必要
- コンパイラ間の互換性がまだ不完全
- モジュールのビルド順序管理が必要
まとめ
C++20モジュールは、長年のヘッダーファイルの問題を解決する革新的な機能です。コンパイラとビルドシステムの対応が進めば、C++開発の標準的な方法になるでしょう。