C++ 標準にはテンプレートに関する新しい規則がいくつか導入されています。そのため、既存のコードが標準から外れたものになってしまう可能性があります。特に、新しいキーワード typename を使用しているコードがこれに該当します。5.0 コンパイラでは、それらの規則はまだ強制はされていませんが、キーワード自体は認識されます。4.2 コンパイラでは、不正なテンプレートコードが一部受け入れられることになり、4.2 コンパイラで動作していたテンプレートコードは、5.0 コンパイラでもおそらく動作します。将来的には新しい規則が適用されるため、開発スケジュールが許すかぎり、既存のコードは新しい C++ 規則に準拠させてください。
C++ 標準には、識別子が型名であるかどうかを判定するための新しい規則が導入されています。次の例で、それらの規則について説明します。
typedef int S; class B { ... typedef int U; ... } template< class T > class C : public B { S s; // OK T t; // OK U x; // 1. C++標準では無効 B::U y; // 2. C++標準では無効 T::V z; // 3. C++標準では無効 };
新しい言語規則では、テンプレート中の型名を解決するために、基底クラス名が自動的に検索されることはないと規定されています。また、キーワードの typename で宣言されていないかぎり、基底クラスやテンプレートパラメータクラスからとられた名前が型名になることはないとも規定されています。
上記の例の最初の無効な行 (1.) では、修飾クラス名とキーワードを使用せずに B から U を型として継承しようとしています。2 行目の無効な行 (2.) では、型は基底クラスの名前によって正しく修飾されてはいますが、typename 修飾子がありません。3 行目の無効な行 (3.) では、テンプレートパラメータからとられた型 V が使用されますが、キーワードの typename が省略されています。この型が基底クラスやテンプレートパラメータのメンバーに依存することはないため、s の定義は有効です。同様に、t の定義では、型の T (型である必要があるテンプレートパラメータ) がそのまま使用されるため、有効になります。
typedef int S; class B { ... typedef int U; ... } template< class T > class C : public B { S s; // OK T t; // OK typename B::U x; // OK typename B::U y; // OK typename T::V z; // OK };
コードを変更するときに問題になるのは、以前は typename がキーワードではなかったということです。既存のコードで typename を識別子として使用している場合は、まず識別子を別の名前に変更する必要があります。
新旧のコンパイラのどちらでもコードがコンパイルされるようにするには、プロジェクト全体で使用されるヘッダーファイルに次の例に示すような文を追加します。
#ifdef TYPENAME_NOT_RECOGNIZED #define typename #endif
これらの行を追加することにより、条件付きで typename が何ものにも置き換えられなくなります。typename を認識しない古いコンパイラ (Sun C++ 4.2 など) を使用する場合は、メークファイル中のコンパイラオプションに -DTYPENAME_NOT_RECOGNIZED を追加してください。
ARM と 4.2 コンパイラには、テンプレート定義を使ってテンプレートを明示的にインスタンス化する標準的な方法がありませんでした。C++ 標準と 5.0 コンパイラの標準モードには、テンプレート定義を使って明示的にインスタンス化する構文 (キーワード template の後に型を宣言する) が追加されています。たとえば、次のコードの最後の行では、デフォルトのテンプレート定義を使って、クラス MyClass を型 int でインスタンス化しています。
template<class T> class MyClass { ... }; template class MyClass<int>; // 明示的なインスタンス化
明示的な特殊化の構文は変更されました。特殊化を明示的に宣言したり、全部の定義をする場合は、宣言の前に template<> を付加してください (空の小なり括弧と大なり括弧が必要です)。たとえば、次のようにします。
// MyClass の特殊化 class MyClass<char>; // 古い形式の宣言 class MyClass<char> { ... }; // 古い形式の定義 template<> class MyClass<char>; // 標準の宣言 template<> class MyClass<char> { ... }; // 標準の定義
これらの形式は、引数のテンプレートに対してプログラマが異なる定義 (特殊化) をどこかで行なっていることを意味します。したがって、コンパイラは、これらの引数に対してはデフォルトのテンプレート定義を使用しません。
5.0 コンパイラの標準モードは、古い構文も旧式の構文として受け付けます。4.2 コンパイラは、新しい特殊化構文を受け付けますが、新しい構文を使用したコードをいつも正しく処理するとは限りません (この機能が 4.2 コンパイラに組み込まれた後に標準が変更されたため)。テンプレート特殊化コードの移植性を最大限に保つためには、プロジェクトのヘッダーファイルに次の例のような文を追加します。
#ifdef OLD_SPECIALIZATION_SYNTAX #define Specialize #else #define Specialize template<> #endif
Specialize class MyClass<char>; // 宣言
サンの C++ テンプレートは、テンプレートインスタンス用のレポジトリ (格納場所) を使用します。C++ 4.2 では、このレポジトリは、Templates.DB というディレクトリに置かれていました。Sun C++ 5.0 では、デフォルトでは、このディレクトリは SunWS_cache と SunWs_config です。SunWS_cashe には作業ファイルが含まれています。SunWS_config には、構成ファイル、特にテンプレートオプションファイル (SunWS_config/CC_tmp1_opt) が含まれています (『C++ ユーザーズガイド』を参照)。
何らかの理由でレポジトリ用のディレクトリの名前を指定したメークファイルがある場合は、手動で修正する必要があります。また、レポジトリの内部構造は変更されているため、Templates.DB の内容にアクセスするメークファイルを使用することはできなくなっています。
おそらく標準的な C++ プログラムは、従来と比べてテンプレートを頻繁に使用します。そのため、複数のプログラムやプロジェクトでディレクトリを共有する場合には注意が必要です。できれば最も簡単な構成にしてください。同じプログラムまたはライブラリに属するファイルは 1 つのディレクトリでコンパイルしなければなりません。これでテンプレートレポジトリは1つのプログラムに適用されます。同じディレクトリで別のプログラムをコンパイルする場合は、CCadmin -clean を使用して、レポジトリを事前に整理してください。詳細は、『C++ ユーザーズガイド』を参照してください。
複数のプログラムで同じディレクトリを共用すると、同じ名前に対して異なる定義が必要になる可能性があります。レポジトリを共有した場合、こうした状況に正しく対処することはできません。
C++ の標準ライブラリには、多数のテンプレートと、それらのテンプレートを使用するための多数の新しい標準ヘッダー名が含まれています。サンの C++ の標準ライブラリでは、テンプレートヘッダーに宣言が置かれ、標準ライブラリのテンプレートはそれぞれ別のファイルに置かれています。このため、プロジェクトファイル名に新しいテンプレートヘッダーと同じものがある場合は、誤ったテンプレートファイルが選択され、多数の意味不明のメッセージが出力される可能性があります。たとえば、ユーザーが vector というテンプレートを独自に作成していて、標準ライブラリのテンプレートは vector.cc というファイルに含まれているとしましょう。ファイルの位置とコマンド行オプションによっては、標準ライブラリの vector.cc が必要なときに、ユーザーが作成した vector.cc が選択されたり、その逆のことが起きたりする可能性があります。コンパイラの将来のリリースで export のようなキーワードが制定され、それを使用するテンプレートが実装された場合この状況はさらに悪くなります。
現在および将来こうした問題が発生するのを防ぐために、以下の 2 つのことをお勧めします。
独自のテンプレートファイル名に標準ヘッダー名を使用しない。
標準ライブラリはすべて名前空間の std に含まれているため、ユーザー作成のテンプレートやクラスと直接的に名前の衝突が起こることはありません。ただし、using 宣言や指令による間接的な衝突の可能性はあるため、標準ライブラリとのテンプレート名の重複は避けるのが賢明です。次に、テンプレートに関係する標準ヘッダーを示します。
algorithm bitset complex deque exception fstream functional iomanip ios iosfwd iostream istream iterator limits list locale map memory numeric ostream queue set sstream stack stdexcept streambuf string typeinfo utility valarray vector
標準ライブラリのテンプレートは、独立したファイルではなくヘッダーファイル (.h) に格納する。 こうすることで、標準ライブラリのファイル名の衝突を防ぐことができます。詳細については『C++ ユーザーズガイド』を参照してください。