チュートリアル: Microsoft Visual C++ でヘッダー ユニットをビルドしてインポートする

この記事は、Visual Studio 2022 を使用するヘッダー ユニットのビルドとインポートに関するものです。 C++ 標準ライブラリ ヘッダーをヘッダー ユニットとしてインポートする方法については、「チュートリアル: STL ライブラリをヘッダー ユニットとしてインポートする」を参照してください。 標準ライブラリをより迅速かつ堅牢にインポートする方法については、「チュートリアル: モジュールを使用して C++ 標準ライブラリをインポートする」を参照してください

ヘッダー ユニットは、プリコンパイル済みヘッダー ファイル (PCH) の代替手段として推奨されます。 ヘッダー ユニットはより簡単に設定して使用でき、ディスク上のサイズが大幅に縮小され、同等のパフォーマンス上の利点が得られます。また、共有 PCHよりも柔軟です。

ヘッダー ユニットと、プログラムに機能を含める他の方法を比較するには、「ヘッダーユニット、モジュール、プリコンパイル済みヘッダーの比較」を参照してください

前提条件

ヘッダー ユニットを使用するには、Visual Studio 2019 16.10 以降が必要です。

ヘッダー ユニットとは

ヘッダー ユニットは、ヘッダー ファイルのバイナリ表現です。 ヘッダー ユニットの拡張子は .ifc です。 名前付きモジュールでも同じ形式が使用されます。

ヘッダー ユニットとヘッダー ファイルの重要な違いは、ヘッダー ユニットがヘッダー ユニットの外部のマクロ定義の影響を受けないことです。 つまり、ヘッダー ユニットの動作が異なるプリプロセッサ シンボルを定義することはできません。 ヘッダー ユニットをインポートするまでに、ヘッダー ユニットは既にコンパイルされています。 これは、ファイルの #include 処理方法とは異なります。 インクルードされたファイルは、ヘッダー ファイルを含むソース ファイルをコンパイルするときにプリプロセッサを通過するため、ヘッダー ファイルの外部にあるマクロ定義の影響を受ける可能性があります。

ヘッダー ユニットは任意の順序でインポートできますが、ヘッダー ファイルには当てはまりません。 1 つのヘッダー ファイルで定義されているマクロ定義が後続のヘッダー ファイルに影響を与える可能性があるため、ヘッダー ファイルの順序が重要です。 1 つのヘッダー ユニットのマクロ定義が、別のヘッダー ユニットに影響を与えることはありません。

ヘッダー ファイルから参照できるすべてのものは、ヘッダー ユニット内で定義されたマクロを含め、ヘッダー ユニットからも参照できます。

ヘッダー ファイルは、インポートする前にヘッダー ユニットに変換する必要があります。 プリコンパイル済みヘッダー ファイル (PCH) よりもヘッダー ユニットの利点は、分散ビルドで使用できることです。 同じコンパイラを使用してインポートし、同じプラットフォームとアーキテクチャを対象とするプログラムをコンパイル .ifc する限り、あるコンピューターで生成されたヘッダー ユニットを別のコンピューターで使用できます。 PCH とは異なり、ヘッダー ユニットが変更されると、そのユニットとそれに依存するもののみが再構築されます。 ヘッダーユニットのサイズ .pchは、最大で 1 桁小さくすることができます。

ヘッダー ユニットは、ヘッダー ユニットを作成し、それを使用するコードを PCH よりもコンパイルするために使用されるコンパイラ スイッチの組み合わせに必要な類似点に対する制約が少なくなります。 ただし、スイッチの組み合わせやマクロ定義によっては、さまざまな変換単位間で 1 つの定義規則 (ODR) の違反が発生する場合があります。

最終的には、ヘッダー ユニットは PCH よりも柔軟なものになります。 PCH では、PCH 内のヘッダーの 1 つのみを取り込むように選択することはできません。コンパイラによってそれらすべてが処理されます。 ヘッダー ユニットでは、それらをまとめてスタティック ライブラリにコンパイルする場合でも、インポートしたヘッダー ユニットの内容のみをアプリケーションに取り込むことになります。

ヘッダー ユニットは、ヘッダー ファイルと C++20 モジュールの間のステップです。 モジュールのいくつかの利点が得られます。 外部のマクロ定義は影響を受けないため、より堅牢であるため、任意の順序でインポートできます。 また、コンパイラではヘッダー ファイルよりも高速に処理できます。 ただし、ヘッダー ユニットにはモジュール内で定義されているマクロが公開されるため、ヘッダー ユニットにはモジュールのすべての利点はありません (モジュールには定義されていません)。 モジュールとは異なり、ヘッダー ユニットでプライベート実装を非表示にする方法はありません。 ヘッダー ファイルを使用したプライベート実装を示すために、先頭のアンダースコアを名前に追加したり、実装名前空間に配置するなど、さまざまな手法が採用されています。 モジュールは、どのような形式でもプライベート実装を公開しないため、これを行う必要はありません。

プリコンパイル済みヘッダーをヘッダー ユニットに置き換えることを検討してください。 あなたは同じ速度の利点を得ますが、他のコード衛生と柔軟性の利点もあります。

ヘッダー ユニットをコンパイルする方法

ファイルをヘッダー ユニットにコンパイルするには、いくつかの方法があります。

  • 共有ヘッダー ユニット プロジェクトをビルドします。 この方法では、組織をより詳細に制御し、インポートされたヘッダー ユニットを再利用できるため、この方法をお勧めします。 必要なヘッダー ユニットを含むスタティック ライブラリ プロジェクトを作成し、それを参照してヘッダー ユニットをインポートします。 この方法のチュートリアルについては、ヘッダー ユニットのヘッダー ユニット スタティック ライブラリ プロジェクトのビルドに関する記述を参照してください。

  • ヘッダー ユニットに変換する個々のファイルを選択する。 この方法では、ヘッダー ユニットとして扱われるものをファイルごとに制御できます。 また、既定の拡張子 (.ixx.cppm.h.hpp) を持たないので、通常はヘッダー ユニットにコンパイルされないファイルをヘッダー ユニットとしてコンパイルする必要がある場合にも便利です。 この方法についてはこのチュートリアルで示します。 開始するには、「方法 1: 特定のファイルをヘッダーユニットに変換する」を参照してください

  • ヘッダー ユニットを自動的にスキャンしてビルドする。 この方法は便利ですが、最適なビルド スループットが保証されないため、より小さなプロジェクトに最適です。 この方法の詳細については、「方法 2: ヘッダー ユニットを自動的にスキャンする」を参照してください。

  • 概要でメンションしたように、STL ヘッダー ファイルをヘッダー ユニットとしてビルドしてインポートし、STL ライブラリ ヘッダーimportをコードを書き直さずに自動的に処理#includeできます。 その方法を確認する場合は、「チュートリアル: STL ライブラリをヘッダー ユニットとしてインポートする」を参照してください。

方法 1: 特定のファイルをヘッダー ユニットに変換する

このセクションでは、ヘッダー ユニットに変換する特定のファイルを選択する方法を示します。 Visual Studio で次の手順を使用して、ヘッダー ファイルをヘッダー ユニットとしてコンパイルします。

  1. 新しい C++ コンソール アプリ プロジェクトを作成します。

  2. ソース ファイルの内容を次のように置き換えます。

    #include "Pythagorean.h"
    
    int main()
    {
        PrintPythagoreanTriple(2,3);
        return 0;
    }
    
  3. Pythagorean.h というヘッダー ファイルを追加してから、その内容をこのコードに置き換えます。

    #ifndef PYTHAGOREAN
    #define PYTHAGOREAN
    
    #include <iostream>
    
    inline void PrintPythagoreanTriple(int a, int b)
    {
        std::cout << "Pythagorean triple a:" << a << " b:" << b << " c:" << a*a + b*b << std::endl;
    }
    #endif
    

プロジェクト プロパティを設定する

ヘッダー ユニットを有効にするには、最初に次の手順で C++ 言語標準/std:c++20 またはそれ以降に設定します。

  1. ソリューション エクスプローラーでプロジェクト名を右クリックし、[プロパティ] を選択します。
  2. プロジェクトのプロパティ ページ ウィンドウの左ペインで、[構成プロパティ]>[全般] を選択します。
  3. [C++ 言語標準] ドロップダウンで、ISO C++20 標準 (/std:c++20) 以降を選択します。 [OK] を選択してダイアログ ボックスを閉じます。

次のようにして、ヘッダー ファイルをヘッダー ユニットとしてコンパイルします。

  1. ソリューション エクスプローラーで、ヘッダー ユニットとしてコンパイルするファイル (この場合は Pythagorean.h) を選択します。 ファイルを右クリックし、[プロパティ] を選択します。

  2. [構成プロパティ]>[一般]>[アイテムの種類] ドロップダウンを [C/C++ コンパイラ] に設定して [Ok] を選択します。

    Screenshot that shows changing the item type to C/C++ compiler.

このチュートリアルの後半でこのプロジェクトをビルドすると、 Pythagorean.h ヘッダー ユニットに変換されます。 このヘッダー ファイルの項目の種類が C/C++ コンパイラ設定され、この方法で設定されるファイルの既定のアクション.h.hppはファイルをヘッダーユニットに変換するため、ヘッダーユニットに変換されます。

Note

このチュートリアルではこれは必須ではありませんが、お客様の情報が提供されます。 たとえば、既定のヘッダー ユニット ファイル拡張子を持たないヘッダー ユニットとしてファイルをコンパイルするには、.cpp構成プロパティ>C/C++Advanced>Compile As を> C++ ヘッダー ユニット (/exportHeader) としてコンパイルするように設定します。Screenshot that shows changing Configuration properties > C/C++ > Advanced > Compile As to Compile as C++ Header Unit (/exportHeader).

ヘッダー ユニットをインポートするようにコードを変更する

  1. サンプル プロジェクトのソース ファイルで、末尾のセミコロンを忘れないでくださいにimport "Pythagorean.h";変更#include "Pythagorean.h"します。 ステートメントには必須 import です。 プロジェクトに対してローカルなディレクトリ内のヘッダー ファイルであるため、次のステートメントimport "file";で引用符をimport使用しました。 独自のプロジェクトで、システム ヘッダーからヘッダー ユニットをコンパイルするには、山かっこを使用します。 import <file>;

  2. メイン メニューの [ビルド]>[ソリューションのビルド] を選択して、ソリューションをビルドします。 これを実行して、予想される出力 (Pythagorean triple a:2 b:3 c:13) が生成されることを確認します。

独自のプロジェクトで、このプロセスを繰り返して、ヘッダー ユニットとしてインポートするヘッダー ファイルをコンパイルします。

ごく少数のヘッダー ファイルをヘッダー ユニットに変換する場合は、この方法をお勧めします。 ただし、コンパイルするヘッダー ファイルが多数あり、ビルド システムで自動的に処理されるためにビルド パフォーマンスが低下する可能性が高い場合は、次のセクションを参照してください。

特に STL ライブラリ ヘッダーをヘッダー ユニットとしてインポートすることに関心がある場合は、「チュートリアル: STL ライブラリをヘッダー ユニットとしてインポートする」を参照してください。

方法 2: ヘッダー ユニットを自動的にスキャンしてビルドする

すべてのソース ファイルのヘッダー ユニットをスキャンし、それらをビルドする時間がかかるため、次のアプローチは小規模なプロジェクトに最適です。 最適なビルド スループットは保証されません。

この方法では、次の 2 つの Visual Studio プロジェクトの設定を組み合わせます。

  • [モジュールの依存関係のソースをスキャンする] では、ビルド システムによってコンパイラが呼び出され、インポートされたモジュールとヘッダー ユニットがすべてビルドされてから、それらに依存するファイルがコンパイルされます。 インポートに含める変換と組み合わせると、ヘッダー ファイルと同じディレクトリにあるファイルにも指定されているheader-units.jsonソースに含まれるヘッダー ファイルは、ヘッダー ユニットにコンパイルされます。
  • [インクルードをインポートに変換する] では、#include で、ヘッダー ユニットとしてコンパイルできるヘッダー ファイルを参照し (header-units.json ファイルで指定されている場合)、コンパイル済みのヘッダー ユニットをヘッダー ファイルで使用できる場合は、ヘッダー ファイルを import として扱います。 それ以外の場合、ヘッダー ファイルは通常の #include として扱われます。 この header-units.json ファイルは、シンボルの重複なしに、各 #includeヘッダー ユニットを自動的にビルドするために使用されます。

これらの設定は、プロジェクトのプロパティで有効にできます。 これを行うには、ソリューション エクスプローラーでプロジェクトを右クリックし、[プロパティ] を選択します。 その後、[構成プロパティ]>[C/C++]>[全般] の順に選択します。

Screenshot that shows the project properties screen with Configuration highlighted and All Configurations selected. Under C/C++ > General, Scan Sources for Module Dependencies is highlighted and set to yes, and Translate Includes to Imports is highlighted and set to Yes (/translateInclude)

[モジュールの依存関係のソースをスキャンする] は、次に示すように [プロジェクトのプロパティ] でプロジェクト内のすべてのファイルに対して、または [ファイルのプロパティ] で個々のファイルに対して設定できます。 モジュールとヘッダー ユニットは常にスキャンされます。 このオプションは、自動的にビルドするヘッダー ユニットをインポートする .cpp ファイルがあり、まだビルドされていない可能性がある場合に設定します。

これらの設定は、次の条件下でヘッダー ユニットを自動的にビルドおよびインポートするために連動します。

  • モジュールの依存関係 のソースをスキャンすると、ヘッダー ユニットとして扱うことができるファイルとその依存関係のソースがスキャンされます。 拡張子.ixxを持つファイル、およびファイル プロパティ>C/C++>Compile As プロパティが C++ ヘッダー ユニットとしてコンパイル (/エクスポート)設定されているファイルは、この設定に関係なく常にスキャンされます。 また、コンパイラによって、ヘッダー ユニットの依存関係を識別する import ステートメントも検索されます。 /translateInclude が指定されている場合、コンパイラによって、ヘッダー ユニットとして扱うために header-units.json ファイルでも指定されている #include ディレクティブもスキャンされます。 依存関係グラフは、プロジェクト内のすべてのモジュールとヘッダー ユニットから構成されます。
  • インクルードをインポートに変換する: コンパイラによって #include ステートメントが検出され、指定されたヘッダー ファイルに一致するヘッダー ユニット ファイル (#include) が存在する場合、コンパイラでは、ヘッダー ファイルを .ifc として扱うのではなく、ヘッダー ユニットをインポートします。 [依存関係のスキャン] と組み合わせると、ヘッダー ユニットにコンパイルできるすべてのヘッダー ファイルが、コンパイラによって検索されます。 許可リストは、ヘッダー ユニットにコンパイルできるヘッダー ファイルを決定するためにコンパイラに参照されます。 このリストは、含まれているファイルと同じディレクトリにある必要がある header-units.json ファイルに格納されます。 Visual Studio のインストール ディレクトリにある header-units.json ファイルの例を参照できます。 たとえば、%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json は、標準テンプレート ライブラリ ヘッダーをヘッダー ユニットにコンパイルできるかどうかを判断するために、コンパイラによって使用されます。 この機能は、ヘッダー ユニットのいくつかの利点を得るために、レガシ コードとの橋渡し役として存在します。

header-units.json ファイルには 2 つの目的があります。 ヘッダー ユニットにコンパイルできるヘッダー ファイルを指定するだけでなく、シンボルの重複を最小限に抑えてビルド スループットを向上させます。 シンボルの重複の詳細については、「C++ header-units.json のリファレンス」を参照してください。

これらのスイッチと header-unit.json では、ヘッダー ユニットのいくつかの利点が得られます。 便利ですが、その代わり、ビルド スループットに影響します。 この方法は、最適なビルド時間が保証されないため、より大きなプロジェクトには最適ではない場合があります。 また、同じヘッダー ファイルが繰り返し再処理される可能性があるため、ビルド時間が長くなります。 しかし、プロジェクトによっては便利な場合があります。

これらの機能はレガシ コード用に設計されています。 新しいコードの場合は、ヘッダー ユニットや #include ファイルではなく、モジュールに移動します。 モジュールの使用に関するチュートリアルについては、「名前付きモジュールのチュートリアル (C++)」を参照してください。

この手法を使用して STL ヘッダー ファイルをヘッダー ユニットとしてインポートする方法の例については、「チュートリアル: STL ライブラリをヘッダー ユニットとしてインポートする」を参照してください。

プリプロセッサへの影響

ヘッダー ユニットを作成して使用するには、標準の C99/C++11 準拠プリプロセッサが必要です。 コンパイラは、任意の形式/exportHeaderが使用されるたびにコマンド ラインに暗黙的に追加/Zc:preprocessorすることで、ヘッダー ユニットをコンパイルするときに、新しい C99/C++11 準拠プリプロセッサを有効にします。 これをオフにしようとすると、コンパイル エラーが発生します。

新しいプリプロセッサを有効にすると、可変マクロの処理に影響します。 詳細については、「可変マクロ解説」セクションを参照してください。

関連項目

/translateInclude
/exportHeader
/headerUnit
header-units.json
ヘッダー ユニット、モジュール、プリコンパイル済みヘッダーを比較する
C++ のモジュールの概要
チュートリアル: モジュールを使用して C++ 標準ライブラリをインポートする
チュートリアル: STL ライブラリをヘッダ ーユニットとしてインポートする