C++ プログラムのファイル編成は、C プログラムの場合よりも慎重に行う必要があります。この章では、ヘッダーファイルとテンプレート定義の設定方法について説明します。
有効なヘッダーファイルを簡単に作成できるとはかぎりません。場合によっては、C と C++ の複数のバージョンで使用可能なヘッダーファイルを作成する必要があります。また、テンプレートを使用するためには、複数回の包含 (べき等) が可能なヘッダーファイルが必要です。
場合によっては、C と C++ の両方のプログラムにインクルード可能なヘッダーファイルを作成する必要があります。ただし、従来の C とも呼ばれる Kernighan & Ritchie C (K&R C) や、ANSI C、『Annotated Reference Manual』C++ (ARM C++)、および ISO C++ では、1 つのヘッダーファイル内の同一のプログラム要素について異なった宣言や定義が規定されていることがあります。言語とバージョンによる違いについての詳細は、『C++ 移行ガイド』を参照してください。これらのどの標準言語でもヘッダーファイルで使用できるようにするには、プリプロセッサマクロ __STDC__ や __cplusplus の定義の有無またはその値に基づいた条件付きコンパイルを使用する必要があります。
__STDC__ マクロは、K&R C では定義されていませんが、ANSI C や C++ では定義されています。このマクロが定義されているかどうかを使用して、K&R C のコードを ANSI C や C++ のコードから区別します。このマクロは、プロトタイプの関数定義とプロトタイプではない関数定義を分離するときに特に役立ちます。
#ifdef __STDC__ int function(char*,...); // C++ & ANSI C declaration #else int function(); // K&R C #endif |
__cplusplus マクロは、C では定義されていませんが、C++ では定義されています。
旧バージョンの C++ では、__cplusplus の代わりに c_plusplus マクロが定義されていました。c_plusplus マクロは、現在のバージョンでは定義されていません。
__cplusplus マクロが定義されているかどうかを使用して、C と C++ を区別します。このマクロは、次のように関数宣言用の extern "C" インタフェースを保護するときに特に便利です。extern "C" の指定の一貫性を保つには、extern "C" のリンケージ指定のスコープ内には #include 指令を含めないでください。
#include “header.h” ... // ... other include files... #if defined(__cplusplus) extern “C” { #endif int g1(); int g2(); int g3() #if defined(__cplusplus) } #endif |
ARM C++ では、__cplusplus マクロの値は 1 です。ISO C++ では、このマクロの値は 199711L (long 定数で表現した、規格の年と月) です。この値の違いを使用して、ARM C++ と ISO C++ を区別します。これらのマクロ値は、テンプレート構文の違いを保護するときに特に役立ちます。
// template function specialization #if __cplusplus < 199711L int power(int,int); // ARM C++ #else template <> int power(int,int); // ISO C++ #endif |
ヘッダーファイルはべき等にしてください。すなわち、同じヘッダーファイルを何回インクルードしても、1 回だけインクルードした場合と効果が同じになるようにしてください。このことは、テンプレートでは特に重要です。べき等を実現するもっともよい方法は、プリプロセッサの条件を設定し、ヘッダーファイルの本体の重複を防止することです。
#ifndef HEADER_H #define HEADER_H /* contents of header file */ #endif |
テンプレート定義は 2 通りの方法で編成することができます。すなわち、テンプレート定義を取り込む方法 (定義取り込み型編成) と、分離する方法 (定義分離型編成) があります。テンプレート定義を取り込んだほうが、テンプレートのコンパイルを制御しやすくなります。
テンプレートの宣言と定義を、そのテンプレートを使用するファイルの中に含める場合、編成が定義の取り込みです。たとえば、次のようにします。
main.cc
template <class Number> Number twice(Number original); template <class Number> Number twice(Number original ) { return original + original; } int main() { return twice<int>(-3); } |
テンプレートを使用するファイルに、テンプレートの宣言と定義の両方を含んだファイルをインクルードした場合も、定義取り込み型編成を使用したことになります。たとえば、次のようにします。
twice.h
#ifndef TWICE_H #define TWICE_H template <class Number> Number twice(Number original); template <class Number> Number twice( Number original ) { return original + original; } #endif |
main.cc
#include “twice.h” int main() { return twice(-3); } |
テンプレートのヘッダーは、べき等にすることが特に重要です。「5.1.2 べき等ヘッダーファイル」を参照してください。
テンプレート定義を編成するもう一つの方法は、テンプレートの定義をテンプレート定義ファイルに記述することです。この例を次に示します。
twice.h
#ifndef TWICE_H #define TWICE_H template <class Number> Number twice(Number original); #endif TWICE_H |
twice.cc
template <class Number> Number twice( Number original ) { return original + original; } |
main.cc
#include “twice.h” int main( ) { return twice<int>( -3 ); } |
テンプレート定義ファイルには、べき等ではないヘッダーファイルをインクルードしてはいけません。また、通常はテンプレート定義ファイルにヘッダーファイルをインクルードする必要はありません。「5.1.2 べき等ヘッダーファイル」を参照してください。なお、テンプレートの定義分離型編成は、すべてのコンパイラでサポートされているわけではありません。
独立した定義ファイルはヘッダーファイルなので、多数のファイルに暗黙のうちにインクルードされることがあります。そのため、テンプレート定義の一部でないかぎり、あらゆる関数と変数はこのファイルに含めないようにします。独立した定義ファイルには、typedef などの型定義を定義できます。
通常、テンプレート定義ファイルには、ソースファイルの拡張子 (.c、.C、.cc、.cpp、.cxx、.c++ のいずれか) を付けますが、このテンプレート定義ファイルはヘッダーファイルです。コンパイラは、これらのファイルを必要に応じて自動的に取り込みます。テンプレート定義ファイルの単独コンパイルは行わないでください。
このように、テンプレートの宣言と定義を別々のファイルで指定した場合は、定義ファイルの内容、その名前、配置先に特に注意する必要があります。さらに、定義ファイルの配置先をコンパイラに明示的に通知する必要もあります。テンプレート定義の検索規則については、「7.5 テンプレート定義の検索」を参照してください。