C++ 規格では、接尾辞のない 10 進整定数は、値が int に収まる場合は int として、そうでない場合は long int として扱うようになっています。値が long int にも収まらない場合の結果は定義されていません。
32 ビットモードの場合、型 int と long のサイズおよびデータ範囲は同じです。1990 C 標準規則に準拠した C++ コンパイラは、INT_MAX+1 潤オ LONG_MAX の範囲にある値を unsigned long として処理します。この処理は、一部のプログラムでは予期しない結果をもたらします。
1999 C 規格では、接尾辞のない 10 進整数に関するこの規則が変更され、unsigned 型として扱われなくなりました。型は、int、long、long long のうちの最初に値を表せる型になります。
標準モードでは、C++ コンパイラはこの C99 規則に従いますが、-compat=4 モードではこれまでどおり C90 の規則に従います。-compat=4 モードでは、コンパイラは C++ 4.2 コンパイラのように動作します。
サイズの大きい 10 進整数を unsigned として扱う場合の移植可能な解決策は、u または U 接尾辞を使用することです。その他の型にも、それぞれ接尾辞を使用することができます。静的関数
// note: 2147483648 == (INT_MAX+1) 2147483648 // (signed) long long 2147483648LL // (signed) long long 2147483648U // same as 2147483648u |
C++ では、あるときは宣言と解釈されたり、またあるときは式と解釈される可能性がある文があります。C++ のあいまい排除規則では、ある文を宣言文とみなすことができる場合は、その文は宣言文とすることになっています。
従来のバージョンのコンパイラでは、次のような事例を誤って解釈していました。
struct S { S(); }; struct T { T( const S& ); }; T v( S() ); // ??? |
このプログラマはおそらく、最後の行で S 型の一時的な値で初期化される変数 v を定義するつもりでした。従来のバージョンのコンパイラは、この文をそのように解釈していました。
しかし、宣言コンテキスト内のコンストラクト、"S()" は、"S 型の値を戻すパラメータのない関数" を意味する抽象宣言子 (識別子のない抽象宣言子) とみなすこともできます。この事例では、関数ポインタ、"S(*)()" に自動的に変換されています。この文はまた、戻り値が T 型で、パラメータが関数ポインタ型の関数 v の宣言としても有効です。
現在ではコンパイラが正しい解釈をするようになったので、このプログラマが意図したようにならない可能性があります。
あいまいにならないようにコードを修正するには、次の 2 通りの方法があります。
T v1( (S()) ); // v1 is an initialized object T v2( S(*)() ); // v2 is a function |
1 行目の 1 対の余分な括弧は、v1 の構文が関数宣言としては不正であるので、"S 型の一時的な値で初期化される T 型のオブジェクト" という意味にしか解釈できません。
同様に、コンストラクト "S(*)()" は値とは考えられないので、関数宣言の意味にしか解釈できません。
最初の行は、次のように書くこともできます。
T v1 = S();
意味は完全に明確になりますが、この初期設定の形式では、通常はそうでもないとはいえ、一時的な値として非常に大きな値が生成されることがあります。
次のようにコーディングするのはお勧めできません。その理由は、意味が不明確で、コンパイラが異なると結果が異なる可能性があるからです。
T v( S() ); // 推奨しない
次のテンプレートの構文は不正ですが、Sun C++ 4 および 5.0 では、エラーになりませんでした。5.1 以降のすべてのバージョンの C++ コンパイラでは、標準モード (デフォルトのモード) のコンパイルで、構文エラーとして報告されます。
template<class T> class MyClass<T> { ... }; // definition error template<class T> class MyClass<T>; // declaration error |
どちらの場合も、MyClass<T> の <T> は無効で、次に示すように削除する必要があります。
template<class T> class MyClass { ... }; // definition template<class T> class MyClass; // declaration |
テンプレートオプションの -instances=static (または -pto) を -xcrossfile や -xipo オプションと組み合わせると、機能しません。この組み合わせを使用したプログラムは、リンクに失敗することがよくあります。
-xcrossfile または -xipo オプションを使用する場合は、デフォルトのテンプレートコンパイルモデルの -instances=global を使用してください。
一般に、-instances=static (および -pto) は使わないでください。使うメリットはすでになく、依然として、『C++ ユーザーズガイド』で説明しているデメリットがあります。
-xlang=f77 コマンド行オプションを使用すると、コンパイルプロセスでリンカーエラーが発生します。エラーを回避するとともに適切な実行時ライブラリをインクルードするには、代わりに -xlang=f77,f90 を使用してコンパイルします。
次の場合に、リンク時に問題が発生することがあります。
const パラメータ付きで宣言されている関数が、別の場所で const パラメータなしで宣言されている。
次に例を示します。
void foo1(const int); void foo1(int); |
これらの宣言は等価ですが、コンパイラは異なる符号化名を付けます。この問題を回避するには、値のパラメータを const として宣言しないでください。たとえば、関数定義の本体などのあらゆる場所で void foo1(int); を使用します。
関数に同じ複合型のパラメータが 2 つあり、一方のパラメータだけ typedef で宣言されている。
次に例を示します。
class T; typedef T x; // foo2 has composite (that is, pointer or array) // parameter types void foo2(T*, T*); void foo2(T*, x*); void foo2(x*, T*); void foo2(x*, x*); |
すべての foo2 宣言は等価で、これらは同じものを符号化する必要があります。しかし、コンパイラは一部に異なった符号化を行なっています。この問題を回避するには、一貫して typedef を使用します。
typedef を一貫して使用できない場合は、回避策として、関数を定義しているファイルに weak シンボルを使用し、宣言とその定義を等価にします。静的関数
#pragma weak "__1_undefined_name" = "__1_defined_name" |
ターゲットアーキテクチャーによって異なる符号化名があります。たとえば、size_t は SPARC V9 アーキテクチャー (m64) では unsigned long ですが、それ以外のアーキテクチャーでは unsigned int です。これは、2 つの異なったバージョンの符号化名がそれぞれ 1 つのモデルに存在するケースです。このような場合は、2 つのプラグマを用意し、適切な #if 指令で制御する必要があります。
互換モード (-compat) では、C++ コンパイラはメンバー関数を指すポインタのリンク名を正しく符号化しません。このエラーのため、復号化プログラムおよび、dbx や c++filt などのデバッグツールから、メンバー関数に余分な先行パラメータ (メンバー関数が属しているクラスタイプを示す) があると報告されます。この問題を解決するには、-Qoption ccfe -abiopt=pmfun1 フラグを追加します。しかし、一般に、このフラグを使用してソースをコンパイルすると、このフラグなしでコンパイルしたソースとの間のバイナリレベルの互換性が失われることがあります。標準モード (デフォルトモード) では、この問題は起きません。
プログラムでテンプレートと静的オブジェクトを使用していると、-instances=extern を指定してコンパイルした場合に未定義シンボルのリンク時エラーが発生します。これは、デフォルト設定の -instances=global では問題になりません。コンパイラは、大域的でない名前空間スコープのオブジェクトに対するテンプレートからの参照をサポートしません。次の例を考えてみましょう。
static int k; template<class T> class C { T foo(T t) { ... k ... } }; |
この例では、テンプレートクラスのメンバーは静的な名前空間スコープ変数を参照します。名前空間スコープはファイルスコープを含むことに注意してください。コンパイラは、静的な名前空間スコープ変数を参照するテンプレートクラスのメンバーをサポートしません。複数のコンパイル単位からテンプレートがインスタンス化されると、各インスタンスは異なる k を参照します。つまり、C++ 単一定義規則違反が発生し、コードは定義されていない動作を起こします。
ユーザーは、k をどのように使用するか、それによってどのような効果を得ようとするかに基づき、次に示す代替方法を実施できます。2 番目のオプションは、クラスのメンバーの関数テンプレートにのみ使用できます。
変数に外部リンケージを持たせる
int k; // not static |
すべてのインスタンスは、k の同じコピーを使用します。
変数をクラスの静的メンバーにする
template<class T> class C { static int k; T foo(T t) { ... k ... } }; |
静的なクラスメンバーは外部リンケージを持ちます。C<T>::foo のインスタンスが使用する k はそれぞれ異なります。C<T>::k のインスタンスは、ほかの関数で共有することができます。通常はこのオプションが使用されます。
名前空間内で #pragma align を使用する場合は、符号化名を使用する必要があります。たとえば、次のコードでは、#pragma align 文は何の働きもしません。この問題を解決するには、#pragma align 文の a、b、および c を符号化された名前に変更します。
namespace foo { #pragma align 8 (a, b, c) // has no effect //use mangled names: #pragma align 8 (__1cDfooBa_, __1cDfooBb_, __1cDfooBc_) static char a; static char b; static char c; } |
C++ コンパイラの従来のリリースでは、C++ 標準の要件に従って関数の多重定義の解決を行いませんでした。今回のリリースでは、多重定義された関数の呼び出しを解決して、多くのバグを修正しています。特に、コンパイラは、呼び出しが実際にあいまいな場合は関数をピッキングしたり、実際にはそうでない場合にも、呼び出しがあいまいであると表示したりする場合がありました。
あいまいであることを示すメッセージに関する回避策には、不要なものもあります。以前には報告されなかった、あいまいに関する新しいエラーが発生しています。
あいまいな関数呼び出しの主な原因の 1 つは、組み込み型のサブセットにさえも多重定義が発生することです。
int f1(short); int f1(float); ... f1(1); // ambiguous, "1" is type int f1(1.0); // ambiguous, "1.0" is type double |
この問題を修正するには、f1 をまったく多重定義しないか、昇格を経験しない各型、つまり int、unsigned int、long、unsigned long、double を多重定義します (long long、unsigned long long、および long double 型がある場合もあります)。
もう 1 つのあいまいに関する主な原因はクラスにおける型変換関数で、特に多重定義された演算子またはコンストラクタが存在する場合です。
class T { public: operator int(); T(int); T operator+(const T&); }; T t; 1 + t // ambiguous |
この演算は、次のように解決できるので、あいまいです
T(1) + t // overloaded operator 1 + t.operator int() // built-in int addition |
多重定義された演算子または型変換関数を使用できますが、両方使用すると、あいまいと判断されます。
実際、型変換関数そのものは、あいまいと判断されたり、意図しなかった場所で変換が発生したりすることがたびたびあります。変換を有効にする必要がある場合は、型変換関数ではなく名前付き関数を使用してください。たとえば、operator int(); の代わりに int to_int(); を使用します。
この変更により、演算子 1 + t はあいまいではなくなります。T(1) + t としか解釈できません。ほかの解釈が必要な場合は、1 + t.to_int() のように記述する必要があります。