ここでは、C++ 3.0 または 3.0.1 コンパイラ用に作成したコードを C++ 5.0 用に移行するときのいくつかの注意事項について説明します。
C++ 3.0 コンパイラ以降に C++ に追加されたキーワードは、次のとおりです。これらのキーワードを識別子として使用している場合は、名前を変更してください。表 3-1 で説明しているように、C++ 5.0 では一部のキーワードを無効にすることができます。
bool、false、true const_cast、dynamic_cast、reinterpret_cast、static_cast explicit export mutable namespace、using typename wchar_t
C++ 3.0 コンパイラ用に作成したコードを C++ 5.0 コンパイラでコンパイルするには、次の変更が必要です。
C++ 5.0 コンパイラでは、K&R 形式の関数定義を使用することはできません。プロトタイプ形式の関数定義を使用してください。
int f(a) int a; { ... } // C++ 5.0 ではエラー
代入で _new_handler 大域変数を設定することはできません。この目的には、set_new_handler() 関数を呼び出してください。
クラス内に operator new() がない場合は、常に大域の operator new() が使用されます。C++ 3.0 では、誤って、大域のものではなくクラスの外側のものが使用される場合があります。次の例では、C++ 3.0 は、領域の割り当てに、誤って Outer::operator new を使用しています。
class Outer { public: void* operator new(size_t); class Inner { ... // operator new なし }; }; Outer::Inner* p = new Outer::Inner; // どちらの operator new か?
typedef 名を、struct、class、union のタグとして使用することはできません。次に例を示します。
typedef struct { int x; } S; struct S b; // C++ 3.0 では問題なく、C++ 5.0 ではエラー S c; // つねに OK
代わりに、構造体、クラス、共用体でタグを使用してください。上記の例のエラーを解決する最も簡単な方法は、typedef 名をさらにタグとして使用する方法です。この方法は、C でも、C++ でも許されます。
typedef struct S { int x; } S; struct S b; // 常に OK S c; // 常に OK
名前をクラス内で使用した場合、その名前を外側のスコープから定義し直すことはできません。大きな問題になる可能性があるため、C++ 標準では、そうした再定義は許可していません。C++ 3.0 コンパイラでは、そのようなコードを検出しませんでしたが、C++ 5.0 ではエラーになります。次に例を示します。
typedef int T; class C { T iv; // int 型 typedef float T; // T を再定義、C++ 5.0 ではエラー T fv; // float 型 };
このエラーを解決するには、T 定義のいずれかの名前を変更します。
C++ 3.0 コンパイラでは、次の例に示すように、定義されていないパラメータを取る関数へのポインタが、状況によっては、「汎用」の関数へのポインタとして機能してしまうという問題がありました。C++ の規則では、関数へのポインタの型は一致している必要があります。
typedef (*pfp)(int,char); typedef (*ufp)(...); int foo(int,char); pfp p = (ufp)foo; // 3.0 では許されるが、5.0 ではエラー
NULL ポインタ定数でコンマを使った式を使用することはできません。リテラルのゼロは NULL ポインタ定数ですが、(anything,0) というような式 (anything は任意の値を示す) は NULL ポインタ定数ではありません。
int f(); char* g() { return (f(), 0); // 3.0 では問題ないが、5.0 ではエラー // 次のようにするか、 // return (f(), (char*)0); // OK // または次の 2 つの文にする // f(); // return 0; }
基底クラスを持つクラスは、集合体の初期化構文を使用して初期化することはできません。C++ 3.0 コンパイラでは、仮想関数が存在していない場合、このことが許されていました。そうしたクラスには、コンストラクタを使用してください。
struct Base { int i; }; struct Derived : Base { int j; }; Derived d = {1, 2}; // 3.0 では問題ないが、5.0 ではエラー
Sun C++ コンパイラのテンプレートは、AT&T の Cfront コンパイラのものとは異なります。Cfront では、リンク時インスタンス化という方式が採用されており、次のようなアルゴリズムになっています。
すべてのユーザーソースファイルをコンパイルする。
プリリンカー (リンクの前処理をするプログラム) を使用して、手順 1 で作成されたすべてのオブジェクトファイルをリンクし、部分リンク済みの実行可能ファイルを作成する。
リンク出力を調べて、ソース中で使用されているテンプレートが存在する、未定義のすべての関数をインスタンス化する。
作成されたすべてのテンプレートと、手順 2 で作成された部分リンク済みの実行可能ファイルをリンクする。
ソース中で使用されているテンプレート関数が存在する、未定義の関数がなくなるまで、手順 3 と 4 を繰り返す。
作成されたすべてのオブジェクトファイルに対してリンクを行う。
リンク時インスタンス化の最大の利点は、特殊化 (インスタンス化されたテンプレート関数を無効にするための、ユーザー提供の関数のこと) を処理するための特別な外部サポートが必要ないという点です。コンパイラによるインスタンス化の対象になるのは、ユーザーソースファイルに定義されていない関数だけです。
しかし、リンク時インスタンス化には次の 2 つの大きな欠点もあります。
リンク段階でインスタンス化が行われるため、インスタンス化で発生したエラーメッセージが、インスタンス化機能を使用した後でしか出力されない。この結果、エラーの発生場所についての有効な追跡手段がない。
プリリンカーを繰り返し呼び出すと、リンク時間が大幅に増大する可能性がある。