Oracle Solaris Studio 12.4 C++ 5.13 コンパイラは、一部の C++ 規則を以前のコンパイラよりも厳密に実行します。以前のコンパイラでは、誤った規則を実行すると一部の有効なコードが不正確に処理されました。このセクションでは、より厳密な実行について説明し、問題を生じさせるコードの例とコードの修正方法を示します。すべての例において、コードは変更でき、そのコードは新旧どちらのコンパイラでも機能するはずです。
以前のコンパイラは、テンプレートがインスタンス化されるまでテンプレート定義を解析しなかったため、次の結果が生じました。
以前のコンパイラは、テンプレートを定義するまで宣言されていない名前の使用を許可していました。明らかな矛盾のせいで無効なコードが受け入れられたり、有効なコードが拒否されたり、または誤った宣言が使用されたりする可能性がありました。
インスタンス化されていないテンプレート内の無制限エラーが診断されませんでした。
T がインスタンス化されるまで、テンプレートパラメータに依存しないテンプレート T の定義内の名前がインスタンス化されませんでした。これらは、T が定義された時点で暗黙的にインスタンス化されるべきです。
例 1:
template< class T > int f(int i = j) // j is not visible { return i; } int j = 2; int main() { return f<int>(); }
以前のコンパイラは、j が定義されるまでテンプレート f が解析されないため、このコードを受け入れていました。
解決策: j の宣言をテンプレートの前に移動します。
例 2:
#include <stdio.h> void f(double d) { printf("f(double)\n"); } template< class C > struct B { B() { f(1); } // f is not dependent on template parameter }; void f(int d) { printf("f(int)\n"); } int main() { B<int> b; // should print "f(double)" }
解決策: テンプレートが依存する宣言がテンプレートの定義より前に行われるようにします。
テンプレート定義内の未修飾名を検索する場合、テンプレートパラメータに依存するベースクラスを調べるべきではありません。以前のコンパイラは誤った名前検索を行なっていました。
例:
template <typename T> struct Base { }; template <typename T> struct Derived : Base <T> { Derived() : Base() { } // unqualified Base should not be found }; Derived <int> x; template <typename T> struct Derived2 : Base <T> { Derived2() : Base<T>() { } // OK }; Derived2<int> x2; int main() { }
クラス Derived では、テンプレート引数なしで未修飾の Base を使用することは有効ではありません。
解決策: クラス Derived2 では、この使用法は適正です。
テンプレートパラメータの名前を再宣言できなくなりました。
例:
template <typename T> class A { typedef int T; }; // re-declare T
解決策: テンプレートパラメータまたは局所名の別の名前を選択します。
template<> なしで明示的特殊化を宣言する 1998 年以前のスタイルは許可されなくなりました。
例:
template <typename T> class A { static T m; }; int A<int>::m = 0; // now an error
解決策:
template<> int A<int>::m = 0;
テンプレート定義の分離を参照してください。「テンプレート定義の分離」コンパイルモデルがより厳密になりました。ヘッダー X.h で宣言されているテンプレートの定義を持つ X.cc ファイルを作成する場合、そのファイル内の項目以外 (つまり、その定義に直接関連付けられている項目以外) を含まないように特に注意が必要です。この規則に違反すると、「複数定義」のエラーが生じやすくなっています。
例:
% cat extdef.h template <typename T> T foo(T); % cat extdef.cc #include "extdef.h" int main() { foo(1); } % CC extdef.cc -template=extdef "extdef.cc", line 3: Error: main() already had a body defined. 1 Error(s) detected.
解決策: テンプレート定義に必要ないすべてのものを X.cc ファイルから削除します。X.cc ファイルは X.h ファイルがインクルードされると常に自動的にインクルードされるため、このファイルの明示的なコンパイルは想定されていないことを思い出してください。
多くの変更が必要な場合は、-template=no%extdef オプションによるコンパイルを検討することもできます。この動作はほかのコンパイラのデフォルトであり、現在はこのリリースのデフォルトです。
C++ では int 型の暗黙的な宣言が許可されたことはありませんが、以前のコンパイラでは警告付きで許可されることがありました。正しいテンプレート処理の妨げとなるため、コンパイラはユーザーが int 型を宣言していると見なさなくなりました。
例:
static i = 0; // now an error
解決策: 宣言で型を明示的に指定します。
関数のクラス C またはクラス T 内のフレンド宣言が T の最初の宣言だった場合、以前のコンパイラは T の宣言を周囲のスコープに誤って挿入していました。有効なプログラムの誤った解釈につながることがあるため、Oracle Solaris Studio 12.4 C++ 5.13 コンパイラではそのようにしなくなりました。
例:
class A { friend class B; // not previously declared friend void foo(); // not previously declared B* bar() // Error: B is not defined. { foo(); // Error: The function "foo" must have a prototype. return 0; } };
解決策: フレンド関数またはクラスを、それをフレンドであると宣言するクラス宣言の前に、クラス外のスコープで宣言します。
class B; void foo(); class A { friend class B; // refers to prior declaration friend void foo(); // refers to prior declaration B* bar() // OK { foo(); // OK return 0; } };
依存関数呼び出しで名前を検索する場合、以前のコンパイラはスコープ内の静的関数を誤って無視していました。Oracle Solaris Studio 12.4 C++ 5.13 コンパイラは、static と extern の関数を同じ方法で処理するようになりました。
例:
// previous compiliers ignored this bar() in dependent name lookup static int bar(int) { return 1; } int bar(long) { return 0; } template <typename T> int foo(T t) { // function call depends on template argument return bar(t); } int main() { return foo(0); }
以前のコンパイラでコンパイルした場合、プログラムは 0 を返していました。Oracle Solaris Studio 12.4 C++ 5.13 コンパイラでは、プログラムは 1 を返します。
名前検索で静的関数のみが見つかった場合、以前のコンパイラは次のようなエラーを返していました。
"Reference to static bar(int) not allowed in template foo(int), try using -features=tmplrefstatic."
extern 関数 bar(long) が削除された前述の例を参照してください。このコードは技術的には C++03 標準の拡張機能であるため、現在、このコンパイラは -compat=5 モードでの警告のみ発行します。C++11 モードでは、このコードは有効な C++11 であるため暗黙的に受け入れられます。
オプション -features=no%tmplrefstatic は引き続き受け入れられますが、テンプレートから静的オブジェクトへの参照が常に許可されているため、実際の影響はありません。