|
バージョン、パッチ、およびサポートQ: 新リリースで C++ コンパイラを確実に識別する方法を教えてください。
A:
すべてのコンパイラでは、コンパイラを識別するマクロが事前定義されています。コンパイラのベンダーは、これらの事前定義済みマクロをリリース間で変更しないようにしていますが、Sun は特にこれらのマクロを不変のパブリックインタフェースとして記録しています。 使用しているコンパイラを確認するには、事前定義済みマクロをテストして用途に適した文字列を出力する簡単なプログラムを作成できます。また、擬似プログラムを作成し、-E (または同等のオプション) を指定してコンパイルする方法もあります。 C++ コンパイラの事前定義済みマクロの一覧については、「C++ ユーザーズガイド」の索引で「マクロ」を検索してください。コンパイラを識別するには、3 桁の 16 進数である __SUNPRO_CC の値を確認します。1 桁目はメジャーリリースです。2 桁目はマイナーリリースです。3 桁目はマイクロリリースです。たとえば、C++ 5.6 は 0x560 になります。 関係のある事前定義済みマクロは次のとおりです。
Q: 「標準」および「旧式」入出力ストリームの違いはなんですか。また、この問題に関する参考文献を教えてください。
A:
2 つのライブラリの設計と実装がまったく異なります。単純な入出力のプログラミングインタフェースは酷似しています。しかし、ユーザー独自のストリームクラスやマニピュレータの書き込みなど、複雑な動作については大きく異なります。
本バージョンの「旧式」入出力ストリームライブラリは、C++ 3.x および 4.x に付属するバージョンと互換性があります。このリリースに付属するマニュアルのほかにも、次のような参考文献があります。
たとえば、次のコードは、Sun のコンパイラを使用して旧式と標準の両方の入出力ストリームで機能します。 ただし、一部のコンパイラは使用されません。 #include public: myclass(int i) :k(i) { } friend ostream& operator<<(ostream&, const myclass&); private: int k; }; // ユーザーが記述する出力演算子 ostream& operator<<(ostream& os, const myclass& m) { os << m.k; return os; } int main() { // cout, cin による単純 I/O cout << "Enter a number:"" << endl; int val; if( !(cin >> val) ) { cout << "Invalid entry, using zero" << endl; val = 0; } // ユーザーが記述する出力演算子の使用 myclass m(val); cout << "Value is " << m << endl; } このコードは次のいずれかの方法で Sun コンパイラを使用してコンパイルされ、実行されます。 example% CC example.cc # 標準の入出力ストリームを使用した標準モード example% CC -library=iostream example.cc # 旧式の入出力ストリームを使用した標準モード example% CC -compat=4 example.cc # C++ 4.2 互換モード Q: どの C++ コンパイラのバージョンに互換性があるのか、見分ける方法を教えてください。
A:
まず、定義しておきます。「上方互換」とは、先行バージョンのコンパイラでコンパイルされたオブジェクトコードが後続バージョンのコンパイラでコンパイルされたコードとリンクできることを意味します。 この場合、最終リンクで最新のコンパイラを使用する必要があります。
C++ 4.0、4.1、および 4.2 コンパイラには上方互換性があります(C++ 4.2 マニュアルに記述されているように、コンパイラのバージョン間にいくつかの「名前の符号化」の問題があります)。
C++ バージョン 5.0 から 5.5 までのコンパイラは、互換モード (-compat) で、4.2 コンパイラとの上方互換です。C++ 4.2 およびバージョン 5.0、5.1、5.2、5.3、および 5.4 で生成された実際のオブジェクトコードは完全に互換性がありますが、後続コンパイラが発するデバッグ情報 (スタブ) は先行デバッガと互換性がありません。 デフォルト標準モードの C++ 5.0 から 5.5 までのコンパイラは、上方互換性があります。実際のオブジェクトコードは完全に互換性がありますが、後続コンパイラの発するデバッグ情報 (スタブ) は先行デバッガと互換性がありません。 Q: Sun Studio ソフトウェア用として、どのような RogueWave ライブラリが「保証」されていますか ?
A:
Sun のコンパイラの各バージョン用に、どのベンダーがどの製品を保証しているのかを確実に追跡することはできません。そして、この FAQ をつねに最新に維持するのも、依然として難しい状態です。C++ コンパイラの特定バージョンについてベンダーが製品テストを行なったかどうかについては、そのベンダーに問い合わせる必要があります。
ただし、Sun のコンパイラに付属している一部の RogueWave ライブラリについては、出荷バージョンと互換性があることを暗黙に保証します。
Q: どのようなパッチがあって、現在のパッチでどのような問題が解決されるのか、どうすれば知ることができるのでしょうか ?
A:
Q: libC.so.5 および libCrun.so.1 に対するパッチは必要でしょうか ?
A:
一般に、Solaris オペレーティングシステムには、これらのライブラリの最新版が付属しています。しかし、しばしば、これらのライブラリには、バグの修正やパフォーマンスの改良のためのパッチが提供されます。そうしたパッチはつねに累積されていて、つねに後方互換であるため、Sun Solve から入手可能な最新のパッチを利用することを推奨します。次の表は、現行のパッチ ID の一覧です。 データベースで最新パッケージを確認してください。パッケージは SUNWlibC (32 ビット) と SUNWlibCx (64 ビット) です。「Advanced Search」ページで「libCrun」を検索してください。検索の条件として、デフォルトの「All Support Resources」ではなく、「Patch Descriptions」を選択します。 >> パッチの表を表示コンパイラの互換性Q: 最近、Solaris オペレーティングシステムにパッチを適用したところ、コードをコンパイルできなくなりました。何が起こったのでしょうか ?
A:
Solaris の最新バージョンで使用可能な新しい数学関数が原因で、以前は有効であったコードが無効になる場合があります。
以前の <math.h> の関数はすべて double 型専用のバージョンでした。新しい Solaris のヘッダーとライブラリには、浮動小数点型と倍長倍精度浮動小数点型の多重定義もあります。あいまいな呼び出しを避けるには、整数の引数を指定してこれらの関数を呼び出すときに明示的なキャストを追加します。次に例を示します。
#include <math.h> 下記の Solaris パッチは、Solaris 8 および 9 用の libm パッチで実現されている ANSI C++ に準拠した <cmath> および <math.h> ライブラリサポートを提供します。
Q: 互換モード (-compat) のコードと標準モードのコードを混在させることはできますか ?
A:
Sun では混在を推奨しません。 「プラグイン」や動的読み込みライブラリであっても、次の理由から、同じプログラム内のコードの混在はサポートしていません。
Q: C++ または C プログラムと F77、F90、または F95 プログラムを組み合わせるには、どうすればよいでしょうか ?
A:
Workshop 6 update 1 (コンパイラのバージョンは 5.2) 以降、-xlang={f90|f95|f77} オプションを使用できるようになりました。このオプションは、リンク行に必要なライブラリとそれらのライブラリの出現順を正確に割り出すようドライバに指示します。
この -xlang オプションは、C コンパイラには使用できません。C ルーチンと Fortran ルーチンを組み合わせるには、cc でコンパイルし、Fortran リンカーでリンクする必要があります。
コード作成と診断Q: foo.cc をコンパイルしていないとき、またはプログラムに foo.cc を組み込んでいないときにファイル foo.cc に関するエラーや警告が発生するのはなぜですか ?
A:
ヘッダーファイル foo.h にテンプレートの宣言がある場合、コンパイラはデフォルトで C++ ファイルの拡張子がある foo ファイル (foo.c、foo.cc、foo.C、foo.cpp、foo.c++) を検索し、見つかった場合は自動的に組み込みます。詳細は、「C++ ユーザーズガイド」の「テンプレート定義の検索」を参照してください。 このように処理しない foo.cc ファイルがある場合は、次のいずれかの操作を行います。
テンプレート定義モデルについては、「C++ ユーザーズガイド」の第 5.2.1 項と第 5.2.2 項を参照してください。また、定義分離型モデルと定義取り込み型モデルについては、「C++ ユーザーズガイド」の索引を検索してください。 Q:
-P 前処理オプションで生成された foo.i ファイルをコンパイルすると、定義が重複するというエラーメッセージが表示されるのはなぜですか ?
A:
デフォルトでは、コンパイラは、テンプレート定義が宣言とは別個になっていても処理できます。テンプレート定義モデルについては、「C++ ユーザーズガイド」の第 5.2.1 項と第 5.2.2 項を参照してください。また、定義分離型モデルと定義取り込み型モデルについては、「C++ ユーザーズガイド」の索引を検索してください。 コンパイラが、.h ファイルまたは .i ファイルで、必要なテンプレートの定義がない宣言を見つけると、テンプレート定義ファイルを検索します。このとき、.h ファイルまたは .i ファイルと同じ名前で拡張子が c、cc、C、c++、または cpp のファイルにテンプレート定義があると見なされます。C++ のマニュアルに説明があるように、この条件を満たすファイルが見つかると自動的に組み込まれます。 -P を指定して foo.cc ファイルをコンパイルして foo.i が生成されたとします。このファイルには、必要なテンプレートの宣言はありますが、定義がありません。コンパイラは定義ファイルを検索し、foo.cc を見つけます。foo.cc ファイルは自動的に組み込まれ、定義が重複します。-template=no%extdef オプションを指定すると、コンパイラによるテンプレート定義の検索を無効にできます。ただし、このオプションを指定すると、別個のテンプレート定義の検索がすべて無効になります。C++ 標準ライブラリは、コンパイラが別個の定義を検索することを必要とします。この例の場合、.i ファイルをコンパイルしたいので、ファイルの名前を変更し、一意の名前にします。すると、別個のテンプレートのコンパイルを無効にする必要がありません。次に例を示します。 CC -P foo.ccmv foo.i foo_prep.i CC -c foo_prep.i -E オプションを使用して前処理ファイルを生成する場合は、この方法を使用できません。-E オプションを使用すると、組み込まれているファイルの名前が記録されるので、.cc ファイルが自動的に検索されます。 Q:
SPARC V9 アーカイブライブラリを動的ライブラリにリンクするとエラーが発生するのはなぜですか。Sun Studio 8 では正常に機能しました。
A:
V9 の新しいデフォルトのコンパイラアドレスのコードモデルは -xcode=abs44 であり、以前の -xcode=abs64 よりもパフォーマンスが向上しています。ただし、この新しいコードモデルは、動的ライブラリ内で使用できません。この問題には、次の 2 つの解決策があります。
Q:
「SunWS_cache: エラー: SunWS_cache のロックの試みが失敗しました」というメッセージが表示される原因は何ですか ?
A:
テンプレートキャッシュの「lock attempt failed (ロックの試みが失敗しました)」というエラーメッセージには、主に次の 2 つの原因が考えられます。
Q:
リンカーで「ld: warning: symbol 'clog' has differing types (ld: 警告: シンボル 'clog' に異なるタイプがあります)」という警告が発生するのはなぜですか ?
A:
同じプログラムで libm.so.2 と「旧式」入出力ストリームライブラリをリンクすると、リンカーで、型が異なる弱いシンボルに関する警告が発生します。この警告は無視できます。 Solaris 10 のデフォルトの数学ライブラリは libm.so.2 で、C99 規格に従って、複素数の自然対数関数 clog がグローバル名前空間に含まれます。-compat=4 または -library=iostream を指定して C++ の旧式入出力ストリームを使用すると、グローバル名前空間内のバッファリングされた標準エラーストリーム clog が取得されます。標準入出力ストリームには、このように競合するシンボルがありません。 Sun は、1 つのプログラムで両方使用できるように、これらの clog シンボルの名前を変更するようにヘッダーとライブラリを変更しました。しかし、元のシンボルを検索する古いバイナリもリンクできるように、元のシンボルのつづりを弱いシンボルとしてライブラリに残す必要があります。 エンティティを自分で宣言せずに、該当するシステムヘッダーを組み込むことで入出力ストリームと数学の宣言を行ってください。 Q:
STLport を使用するマルチスレッドプログラムを -xarch=v8plus または -xarch=v8plusa を指定してコンパイルするとクラッシュするのはなぜですか ?
A:
STLport には、-xarch=v8plus または -xarch=v8plusa を指定してコンパイルすると正しくマルチスレッド処理が行われないバグがありました。このバグは修正されましたが、修正時に STLport のヘッダーと、STLport の一部のオブジェクトが変更されました。STLport の旧ヘッダーを使用してコンパイルされたコードを新しいプログラムにリンクするには、C++ 5.6 またはパッチを適用した旧リリースで再コンパイルする必要があります。
Q:
abs() の呼び出しがあいまいである、というのはなぜですか ?
A:
C++ 規格の第 26.5 項で、abs 関数の次の多重定義が定められています。
最近まで、Solaris で使用可能な abs は、従来の int 用だけでした。どのような数値型で abs を呼び出しても、値は、<stdlib.h> または <cstdlib> がインクルードされているものとして、暗黙で int 型に変換され、int 版の abs が呼び出されました。 Solaris のヘッダーとライブラリは最近、更新され、数学関数に関する C++ 規格に準拠するようになりました。 たとえば、<stdlib.h> ではなく、<math.h> をインクルードして、整数型の引数で abs を呼び出した場合、コンパイラは、3 つある浮動小数点型バージョンの abs 関数のうちの 1 つを選択する必要があります。整数値は任意の浮動小数点型に変換でき、どの変換の優先順位が高いということもありません。参照:C++ 規格の第 13.3.3 項。したがってこの関数呼び出しはあいまいです。C++ 規格に準拠するすべてのコンパイラであいまいであるとのエラーが発生します。 整数型の引数で abs 関数を呼び出す場合、その適切な宣言を使用されるようにするには、標準ヘッダーの <stdlib.h> または <cstdlib> をインクルードします。浮動小数点型の値で abs を呼び出す場合は、<math.h> または <cmath> もインクルードします。 簡単なプログラミングの習慣として、<math.h> か <cmath> をインクルードする場合は、<stdlib.h> または <cstdlib> もインクルードすることを推奨します。 cos や sqrt などのほかの数学関数にも同じ問題があります。Solaris のヘッダーとライブラリは C++ 規格に準拠するようになり、浮動小数点、倍精度浮動小数点、倍長倍精度浮動小数点用の関数の多重定義があります。たとえば、整数値を指定して sqrt を呼び出す場合、以前は整数用の sqrt しかなかったので、コードはコンパイルできました。現在は、浮動小数点用の 3 つの関数があるので、整数値をいずれかの浮動小数点型にキャストする必要があります。
double root_2 = sqrt(2); // エラー Q:
一時オブジェクトはいつ破棄されるのですか ?
A:
コンパイラは、便宜上、または言語規則で定められているため、一時オブジェクトを作成することがあります。たとえば、関数が返す値や、型変換の結果は一時オブジェクトです。 当初の C++ の規則では、一時オブジェクト (temp) は、オブジェクトが作成されたブロックの末尾までの間でいつでも破棄できました。Sun の C++ コンパイラは、ブロックの末尾 (中括弧の閉じ括弧) で temp を破棄していました。 数年間に及ぶ議論の末、C++ 委員会で temp を破棄する場所が決定しました。temp が作成されている完全な式の末尾です。これは通常、式がある文の末尾です。これが C++ 規格の規則です。 Sun の C++ を使用する多くのプログラムでは、おそらく意図せずに temp をブロックの末尾まで必要としていることがわかりました。したがって、Sun はコンパイラのデフォルトの動作を変更しませんでした。一時オブジェクトはデフォルトで、オブジェクトが作成されたブロックの末尾で破棄されます。 動作を規格に準拠させ、temp を完全な式の末尾で破棄する場合は、コンパイル時オプション -features=tmplife を使用します。 このオプションは、プログラム全体で一貫して使用する必要はありません。モジュールで作成される一時オブジェクトは、コンパイル時に有効であったオプションに従って、式の末尾、またはブロックの末尾で破棄されます。 Q:
標準例外クラスについてあいまいさが報告されるのはなぜですか ?
A:
Solaris の標準ヘッダー <math.h> には、標準 UNIX の要求どおり、構造体「exception」の宣言があります。using 宣言または using 指令を使用して C++ 標準例外クラスを大域スコープに入れると、衝突が発生します。
// 例 1 #include <math.h> #include <exception> using namespace std; // using 宣言 exception E; // エラー、あいまいな例外 // 例 2: #include <math.h> #include <exception> using std::exception; // using 指令 exception E; // エラー、例外に対する多重宣言 using 宣言 と using 指令を比較すると、名前解決は微妙に異なります。 そのため、エラーメッセージは完全に一致しません。 解決策:
Q:
派生仮想関数でのスロー (throw) 指定について C++ 5.3 がエラーメッセージを表示するのはなぜですか ?
A:
5.3 C++ コンパイラが新たに強制する C++ 規則では、派生クラスの仮想関数は、上書きされる関数が許容する例外のみを許容できます。上書き関数の制限を強化することはできますが、制限を緩めることはできません。次の例で考えてみてください。
class Base { public: // int 例外をスローする可能性。 ほかは無し virtual void f() throw(int); }; class Der1 :public Base { public: virtual void f() throw(int); // OK。 同一指定 }; class Der2 :public Base { public: virtual void f() throw(); // OK。 制限強化 }; class Der3 :public Base { public: virtual void f() throw(int, long); // エラー。 long 不可 }; class Der4 :public Base { public: virtual void f() throw(char*); // エラー。 char* 不可 }; class Der5 :public Base { public: virtual void f(); // エラー。 すべての例外を許可 }; 次のコードは、C++ 規則の施行理由を示しています。 #include "base.h" // Base クラスの宣言 void foo(Base* bp) throw() { try { bp->f(); } catch(int) { } } Base::f() は、int 例外だけをスローするように宣言されているので、関数 foo は int 例外をキャッチし、例外を逃さないと宣言できます。あとで誰かが、上書き関数が任意の例外をスローできるクラス Der5 を宣言し、Der5 のポインタを foo に渡したとします。foo 関数は、コンパイル時にコードに問題がなくても、無効になります。 Q:
自分のプログラムをリンクするとテンプレートインスタンスが失われるのはなぜですか。インスタンスはテンプレートキャッシュの中にあるようです。
A:
C++ 5.5 以降、コンパイラは、デフォルトの設定ではテンプレートキャッシュを使用しません。したがって、C++ 5.5 かそれ以降にコンパイラをアップグレードし、デフォルトのテンプレートコンパイルモデルを使用するために、-instances=global を指定してコードを再コンパイルすることを推奨します。 テンプレートキャッシュは、コンパイラが生成するオブジェクトファイル間の依存関係リストを保有し、テンプレートインスタンスはキャッシュに含まれています。ただし、現在のコンパイラは、-instances=extern が指定されている場合にのみテンプレートキャッシュを使用するようになっていることに注意してください。オブジェクトファイルを移動または名前変更するか、オブジェクトファイルをライブラリに結合した場合、キャッシュへの接続が失われます。2 つの代替手段を次に示します。
Q:
+w2 を使用しているとき、または +w2 +d を使用していないときに、関数は展開されないという警告メッセージが表示されるのはなぜですか ?
A:
C++ コンパイラには、次の 2 種類のインライン化があります。パーサーによって行われる C++ の inline 関数のインライン化と、コードジェネレータによって行われる最適化のインライン化です。C および Fortran コンパイラには、最適化のインライン化だけしかありません(1 つのプラットフォームでは、すべてのコンパイラに対して同じコードジェネレータが使用されます)。
C++ コンパイラのパーサーは、暗黙にあるいは明示的に inline として宣言されたすべての関数のインライン化を展開しようと試みます。関数が大きすぎると、パーサーは、+w2 オプションを使用している場合にだけ警告を発します。+d オプションは、パーサーが関数をインライン化しないようにします。これが、+d を使用すると警告メッセージが表示されない理由です (また、-g オプションも、C++ インライン関数のインライン化を止めます)。-xO オプションは、このタイプのインライン化には何も影響を与えません。
最適化のインライン化は、プログラム言語に左右されません。-xO4 またはそれより高い最適化レベルを選択すると、コードジェネレータは、関数がソースコード内でどのように宣言されていようとも、すべての関数を検査して、置き換える利点があれば、関数呼び出しをインラインコードで置き換えます。最適化のインライン化 (あるいは、関数のインライン化の失敗) に関するメッセージは何も表示されません。+d オプションは、最適化のインライン化には何も影響を与えません。
Q:
-ptr オプションを使って、複数のテンプレートリポジトリを使用したり、複数のプロジェクト間でリポジトリを共有したりできますか。できない場合は、どのようにすればよいでしょうか ?
A:
-ptr オプションは、バージョン 5.0 〜 5.6 ではサポートされていません。 バージョン 4.2 で提供されていましたが、必ずしも ユーザーの期待どおりに機能せず、多くの問題を発生させていました。 異なるプロジェクト間でリポジトリを共有しないことが最善の方法です。リポジトリを共有すると、解決しようとしている問題よりも深刻な問題が発生する可能性が高くなります。1 つのディレクトリで 1 つのプロジェクトだけをコンパイルします。異なるプロジェクトに関連付けられているバイナリには異なるディレクトリを使用します。 バージョン 5.0 以降では、コンパイラが、生成されているオブジェクトファイルと同じディレクトリにテンプレートリポジトリを置きます。1 つのプロジェクトに複数のリポジトリを使用する場合は、そのリポジトリを置くディレクトリにオブジェクトファイルを生成します。リンク時には、オブジェクトファイルに関連付けられているすべてのリポジトリでテンプレートインスタンスが自動的に検索されます。コンパイラオプションは不要です。 Q:
fprintf("%s",NULL) がなぜセグメント例外の原因になるのですか ?
A:
アプリケーションの中には、NULL 文字ポインタは空文字列へのポインタと同じ扱いをしなければならないと誤って認識しているものがあります。これらのアプリケーションでは、NULL 文字ポインタがアクセスされるとセグメント違反が発生します。
いくつかの理由により、 *printf() の関数ファミリには NULL ポインタのチェック機能がありません。これだけに限りませんが、次のようなことが挙げられます。
Q:
sqrt() の呼び出し方法によって、複素数の平方根の虚部の符号が異なります。これはなぜですか ?
A:
この関数の実装は、「C99 csqrt Annex G 仕様」で規定されています。たとえば、次のコード例の出力を見てください。
complex sqrt (3.87267e-17, 0.632456)
float sqrt (3.87267e-17, -0.632456)
Q:
クラステンプレート中のフレンド関数はインスタンス化されず、リンク時エラーになります。C++ 5.0 ではこのようなことはありませんでした。 今回のバージョンでエラーになるのはなぜですか ?
A:
次のテストケースは、C++ 5.0 コンパイラでは、エラーなしにコンパイルおよびリンクが行われますが、以降のバージョンのコンパイラでは、リンク時エラーが発生します。
example% cat t.c
#include <ostream>
using std::ostream;
template <class T>
class TList { public: friend ostream& operator<< (ostream&, const TList&); }; template <class T> ostream& operator<< (ostream& os, const TList<T>& l) { return os; } class OrderedEntityList { public: TList<int> *Items; ostream& Print(ostream &) const; }; ostream& OrderedEntityList::Print(ostream& os) const { os << *Items; return os; } main() { } example% CC t.c 未定義の最初に参照している シンボル (ファイル内) std::basic_ostream<char,std::char_traits<char> >&operator<<(std::basic_ostream<char,std::char_traits<char< <&,const TList<int>&) 4421826.o ld:重大なエラー:シンボル参照エラー。a.out に書き込まれる出力はありません。 規格によると、このテストケースは無効です。問題点は以下の宣言にあります。 friend ostream& operator<< (ostream&, const TList&); 上の宣言は、いずれのテンプレートインスタンスも参照していません。 非修飾名の参照は、フレンド宣言の 1 箇所で認識可能であるとしても、テンプレート宣言と一致しません。フレンド宣言をテンプレートと一致させるには、フレンド宣言をテンプレート関数として宣言するか、または名前を修飾する必要があります。どちらの方法でも、テンプレート用の宣言は、フレンド宣言の 1 箇所で認識可能である必要があります。 簡単に言うと、フレンド宣言はテンプレートを参照しませんが、関数呼び出しに最も一致する関数を宣言します(ほかの点で等しいのであれば、テンプレート関数よりもテンプレートでない関数の方が望ましいと言えます)。 次のコードは有効です。 template <class T> class TList; // so we can declare the operator<< template template <class T> ostream& operator<< (ostream& os, const TList<T>& l) { return os; } template <class T> class TList { public: // 関数名のスコープ修飾に注意 friend ostream& ::operator<< (ostream&, const TList&); }; Q:
入れ子になったクラスから包含するクラスのメンバーにアクセスできない、というのはなぜですか ?
A:
class Outer { ARM および C++ 標準によると、入れ子になったクラスは、包含するクラスのメンバーに特別にアクセスすることはありません。my_int および k は Outer 内で非公開なので、Outer のフレンドだけがメンバーにアクセスできます。入れ子になったクラスをフレンドにするためには、クラスを前もって宣言してからフレンドにする必要があります。 以下に例を示します。 class Outer { class Inner; friend class Inner; class Inner { my_int j; // OK int foo() { return k; // OK } }; }; Q:
実行時に「pure virtual function call (純粋仮想関数呼び出し)」メッセージが表示される原因は何ですか ?
A:
プログラム内に、あるエラーが発生すると必ず、「pure virtual function call (純粋仮想関数呼び出し)」メッセージが表示されます。このエラーは、次の 2 つのどちらかの場合に起こります。
Q:
派生クラスの仮想関数は、識別形式の異なる基底クラスの仮想関数を隠す、というのはなぜですか。ほかのコンパイラの場合、このコードには何も問題がありません。
A:
C++ の規則では、多重定義は 1 スコープ内でのみ認められ、スコープを超えるものは認められません。基底クラスは、派生クラスのスコープを取り巻くスコープ内にあると考えられます。そのため、派生クラス内で宣言された名前は、基底クラス内のあらゆる関数を隠し、多重定義できません。この基本的な C++ 規則は、ARM よりも旧式です。
ほかのコンパイラが、何も異常を表示しないとしても、これにより損害を被ります。 なぜなら、コードは、期待するとおりには動作しないからです。Sun のコンパイラは、そのコードを受け付けるときに警告を表示します(正当なコードですが、期待どおりに動作する可能性は低いと思われます)。
多重定義されたセットに基底クラス関数を含めたい場合は、基底クラス関数を現在のスコープに入れるために手を加える必要があります。デフォルトの標準モードでコンパイルを行っている場合は、宣言の使用を追加できます。
class Base { ライブラリの互換性Q: C++ コンパイラの実行時ライブラリから別の機能を得るために変更できるライブラリ構成マクロはどれですか ?
A:
ライブラリ構成マクロの定義、定義解除、変更は、一切しないでください。ライブラリヘッダは、ライブラリが構築された方法に一致する必要があります。そうでない場合、プログラムのコンパイルやリンクができず、プログラムが正しく動作しなくなるおそれがあります。
Q: -I オプションと -L オプションを使用する必要があるのは、どのようなときですか ?
A:
-I オプションを指定するのは、プロジェクトのヘッダファイルが、それらをインクルードするファイルと同じディレクトリに入ってないときに、それらの入っているディレクトリを指す場合か、取得する他社製のライブラリ用のヘッダファイルが入っているディレクトリを指す場合です。-L オプションを指定するのは、構築するライブラリまたは取得する他社製のライブラリが入っているディレクトリを指す場合です。
/usr/include やコンパイラインストール領域を指すのに -I を使用しないでください。また、/lib、/usr/lib、またはコンパイラインストール領域を指すのに -L を使用しないでください。CC コンパイラドライバは、システムヘッダとライブラリの置かれている場所を認識しており、正しい検索順序に従います。システムディレクトリを指す -I オプションまたは -L オプションを使用すると、コンパイラが誤ったヘッダやライブラリを検出する場合があります。 Q:
完全準拠の C++ 標準ライブラリ (stdlib) の入手方法を教えてください。あるいは、現在の libCstd がサポートしていない機能には、どのようなものがありますか ?
A:
本リリースには、STLport の Standard Library 実装のバージョン 4.5.3 が、オプションの標準ライブラリとして含まれています。STLport は C++ 規格に厳密に準拠していながら、一般的な拡張機能も保持しています。ただし、デフォルトで使用される標準ライブラリとのバイナリ互換性はありません。
現在の libCstd は、バージョン 5.0 の C++ コンパイラ用に開発されました。このバージョンは、クラスのメンバーとしてテンプレートをサポートしていません。標準ライブラリの一部には、メンバーテンプレートが必要であり、このことは一部機能が失われることを意味します。これは特に、暗黙の型変換を可能にするコンストラクタテンプレートを持つコンテナクラスにおいて起こります。その場合は、回避策としてソースコード内に明示的な型変換を記述する必要があります。
バージョン 5.1 以降、C++ コンパイラはクラスのメンバーとしてテンプレートをサポートしていて、規格に準拠したライブラリを使用できます。ソースとバイナリレベルの互換性を損なうことなくライブラリを更新することはできないため、Sun では、同じ制限で libCstd を引き続き出荷しています。
gnu と SGI の Web サイトでは、パブリック版の標準ライブラリが配布されています。 また、RogueWave、Dinkumware などのベンダーからライブラリを購入することもできます。STL については、次の質問を参照してください。
Q:
C++ 標準テンプレートライブラリ (STL) が必要です。どこで入手できるのでしょうか。互換モード (-compat) 用のものはあるのでしょうか ?
A:
C++ コンパイラは現在、STLport の Standard Library 実装バージョン 4.5.3 をサポートしています。libCstd が依然としてデフォルトライブラリですが、代わりに STLport の製品を使用することもできます。このリリースには、静的アーカイブである libstlport.a と動的アーカイブである libstlport.so の両方が含まれています。
次のコンパイラオプションを指定すると、libCstd を無効にして STLport を使用できます。
-library=stlport4 デフォルトの C++ 標準ライブラリ libCstd と STLport の両方が STL を含んでいます。別のバージョンの標準ライブラリを使用できますが、危険が伴い、良好な結果を保証できません。 別の STL をプラグインするには、 -library=no%Cstd オプションを使用して、コンパイラが実際に使用するヘッダーファイルおよびライブラリを見つけられるようにします。交換用のライブラリに専用の iostreams がなく、標準の iostreams の代わりに「従来」の iostreams を使用できる場合は、コマンド行に -library=iostream を追加します。詳細な手順については、コンパイラに付属する「C++ ユーザーズガイド」の「C++ 標準ライブラリの置き換え」を参照してください。このガイドは、オンラインで利用できます。 Q:
libCstd で失われた標準ライブラリ機能にはどのようなものがありますか ?
A:
標準ライブラリは、本来 (C++ 5.0 では)、コンパイラ中にメンバーテンプレートおよび部分的な特殊化を必要とする機能をサポートせずに構築されたものです。これらの機能は C++ 5.1 以降、使用可能ですが、標準ライブラリ内でオンにすることはできません。 これらは下方互換性を残しているからです。下記は、無効にされた機能の失われた機能リストです。
無効にされた機能: メンバーテンプレート関数
無効にされた機能: メンバーテンプレートクラス
無効にされた機能: 部分的に特殊化されている関数テンプレートの引数の多重定義
無効にされた機能: デフォルトのパラメータを使ったテンプレートクラスの部分特殊化
Q:
標準ライブラリの機能が失われることで、どのような影響があるのでしょうか ?
A:
C++ 規格では正当なコードがコンパイルされないことがあります。 もっともよくあるのは、ペアの第 1 要素はconst であるのに、そのようには宣言されていないマップを作成している場合です。メンバーコンストラクタテンプレートは、必要に応じ暗黙で pair<T, U> を pair<const T, U> に変換します。しかし、コンストラクタがないために、変換されずに、コンパイルエラーになります。
マップ内のペアの第 1 要素を変更することはできないため、もっとも簡単な解決策は、ペア型を作成するときに明示的な Q:
標準ストリームで機能する tools7 ライブラリのバージョンはありますか。あるいは tools8 はまもなく入手できるようになるのでしょうか ?
A:
はい、あります。 ただし、C++ 5.3、5.4、5.5 および 5.6 のみです。-library=rwtools7_std コマンドを使用して
このライブラリとリンクしてください。
RogueWave は、その SourcePro 製品の一部としてのみ、 Tools.h++ を提供するようになりました。そのような理由から、Tools.h++ バージョン 8 は存在しません。 コンパイル時のパフォーマンスQ:
最近 Sun Open Network Environment (Sun ONE) Studio 8 C++ 5.5 コンパイラにアップグレードし、コンパイル中のリンク時間が大幅に増加していることに気付きました。この原因は何でしょうか。また、回避策はあるでしょうか?
A:
リンク時間が大幅に増加する原因として 2 つのことが考えられます。 もっとも可能性の高い原因は、コンパイラがインライン関数をライン外に生成する方法が変更されたことです。次のいずれかの条件が True の場合、コンパイラはインライン関数を通常の呼び出し可能な関数 (ライン外) として生成します。
Sun ONE Studio 8 の C++ 5.5 より前のコンパイラは、インライン関数を行外で生成するときに、関数を必要とする各モジュール内に関数のローカル (外部ではない) コピーを作成しました。この方法は、コードが増大し、また C++ 規格に準拠していないという問題がありました。 C++ 規格では、最終的なプログラムにある静的ではないインライン関数が 1 つだけである必要があると定められています。インライン関数のアドレスを取得する場合、関数は行外で生成される必要があります。2 つの異なるモジュールでアドレスを取得する場合、アドレスが異なり、プログラムの動作に影響する可能性があります。 Sun ONE Studio 8 の C++ 5.5 以降のコンパイラは、行外のインライン関数を、デフォルトでテンプレートインスタンスを生成するのと同じ方法で、大域関数として comdat セクションに生成します。 この変更の利点は、最終的なプログラムが小さくなること、およびプログラムが C++ 規格に準拠することです。 欠点は、プログラムに行外のインライン関数が多数あるときにリンカーの処理量が多くなることです。リンカーは、以前は認識できなかったこれらの関数を外部シンボルとして処理し、複製を破棄する必要があります。 「-features=no%extinl」と指定すると、インライン関数に対するコンパイラの以前の動作に戻すことができます。この変更によって問題が解決する場合は、プログラムを小さくするか、リンク時間を短縮するかの間で選択できます (ただし、インライン関数のコピーが複数できます)。 リンク時間が長くなる 2 つ目の原因は、プログラムで多数のテンプレートインスタンスを生成するかどうかに関係があります。C++ コンパイラのバージョン 5.5 以降でのテンプレート処理方法の変更が原因でリンク時間が長くなっている可能性があります。 ほとんどの場合、テンプレートキャッシュに 1 つのコピーを維持するよりも、各 .o ファイルに大域 comdat のインスタンスを直接作成する方がコンパイル時間もリンク時間も短くなります。ただし、数が少ない同じテンプレートインスタンスを多数の異なるモジュールで使用するプログラムは、テンプレートキャッシュを使用した方がリンク時間が短くなる可能性があります。プログラムで多数のテンプレートを使用し、インライン関数の以前の動作に戻してもリンク時間が変わらない場合は、再構築と -instances=extern オプションの使用を試してみてください。このオプションは、C++ 5.5 より前のデフォルトでした。 Q:
C++ コンパイラのプリコンパイル済みヘッダー機能を使って開始してもコンパイル時間が短縮されないのはなぜですか ?
A:
プリコンパイル済みヘッダーを使用するとコンパイル時間が必ず短縮するわけではありません。プリコンパイル済みヘッダーを使用すると、ファイルを直接コンパイルするときには発生しないオーバーヘッドが発生します。プリコンパイル済みヘッダーを使用してパフォーマンスを向上するには、プリコンパイル済みヘッダーに、プリコンパイルによって取り除くことができる冗長性が必要です。
たとえば、プリコンパイルすることでメリットが得られる可能性が高いプログラムとしては、多数のシステムヘッダーや入出力ストリーム、STL ヘッダー、プロジェクトヘッダーを含んでいるプログラムがあります。これらのファイルには、条件付きでコンパイルされるコードが含まれるからです。一部のヘッダーは複数回組み込まれていますが、コンパイラは、ファイル全体を読み取らないと、インクルードが冗長で、処理の必要がないことがわかりません。システムヘッダーには一般に展開するマクロが数百個あります。 プリコンパイル済みヘッダーを使用すると、ファイルを十数個開かなくても、1 ファイルだけで済みます。処理の必要がない複数のインクルード、およびコメントや余分な空白は取り除かれます。ヘッダー内のマクロは事前に展開されます。これらの処理によって通常はコンパイル時間が大幅に短縮されます。 Q:
大きなファイルのコンパイルに要する時間が、小さなファイルよりも大幅に長いのはなぜですか ?
A:
問題は、ファイルの大きさではない可能性があります。コンパイル時間が長い原因として、次の 3 点が考えられます。
Q:
バージョン 4.2 と比べて、バージョン 5.0 および 5.1 のコンパイラのコンパイル時間は大幅に長くなっています。将来、この問題は解決されるのでしょうか ?
A:
5.1 のパッチ 01 から現行バージョンまででは、コンパイル時間が大幅に改善されています。コンパイラのパフォーマンスに満足できない場合、次の推奨事項に留意してください。
Q:
バージョン 4.2 のコンパイラと比べてバイナリのサイズがかなり大きくなります。この問題の解決策はあるのでしょうか ?
A:
-g オプションでコンパイルを行うと、バージョン 5.0 からコンパイラがテンプレートのデバッグ用の大量の情報を書き出すため、バイナリのサイズが大きくなります。バージョン 5.1 では、多くの種類のプログラムについて、そうしたデバッグ情報のサイズは大幅に縮小されます。バージョン 5.2 から現行のコンパイラまででは、さらに改善が加えられました。多くの場合、バイナリのサイズは 25% から 50% 超まで小さくなります。こうした改良は、コードで名前空間やテンプレート、多数の継承レベルを持つクラス階層が使用されている場合に特に顕著です。
Q:
1 つのコンパイルプロセスを複数のプロセッサに分散できるでしょうか。一般的に、いつでもマルチプロセッサ (MP) システムの方がコンパイル時のパフォーマンスは良いのでしょうか ?
A:
コンパイラそのものはマルチスレッド化されていません。しかし、コンパイラは 1 回のコンパイルで常に多数のほかのプロセスを同時に動作させるため、MP システムの方がパフォーマンスの向上を期待できます。
dmake (コンパイラに付属しているツールの 1 つ) を使用すると、複数のコンパイルを同時に実行できます。
実行時のパフォーマンスQ:
標準ライブラリのストリームは、gcc または KAI ストリームより低速です。パフォーマンスの低下が大きすぎます。 解決策はあるのでしょうか ?
A:
この問題を解決するには、C++ コンパイラの新しいオプション -sync_stdio=no をリンク時に指定するか、sync_with_stdio(false) 関数の呼び出しを追加して再コンパイルします。
stdlib 2.1.1 の最大のパフォーマンスの問題は、デフォルトで C の stdio と C++ のストリームの同期がとられることです。cout への出力はすぐにフラッシュされます。プログラムで cout への出力が多く、stdout への出力は少ない場合、余分なバッファーのフラッシュが、プログラムの実行時のパフォーマンスに大きく影響します。C++ 規格では、この動作が定められていますが、この規格に準拠していない実装もあります。次のプログラムが、同期の問題を表しています。このプログラムでは、「Hello beautiful world」と改行を出力する必要があります。 #include <iostream> #include <stdio.h> int main() { std::cout << "Hello "; printf("beautiful "); std::cout << "world"; printf("\n"); } cout と stdout が別々にバッファーに入れられた場合、出力が混ざります。 実行可能ファイルを再コンパイルできない場合は、C++ コンパイラの新しいオプション -sync_stdio=no をリンク時に指定します。このオプションを指定すると、プログラム出力が発生する前に sync_with_stdio( ) がプログラムの初期化時に呼び出されます。 再コンパイルできる場合は、プログラム出力が発生する前に sync_with_stdio(false) 関数の呼び出しを追加し、出力を同期しないことを指定します。呼び出し例を次に示します。 #include int main(int argc, char** argv) { std::ios::sync_with_stdio(false); } sync_with_stdio の呼び出しが、プログラムで最初の呼び出しである必要があります。 -sync_stdio については、『C++ ユーザーズガイド』または C++ のマニュアルページ CC(1) を参照してください。 Q:
C++ は、"inline" キーワードの付いた関数を常にインライン化するのでしょうか ? あるいは、そのように記述したとしても、インライン化されない関数があるのはどうしてでしょうか ?
A:
基本的に、コンパイラは、インライン宣言を指令とみなし、そのように宣言された関数をインライン化しようとします。バージョン 5.1 から 5.6 までのコンパイラでは、インライン化アルゴリズムが改良され、より多くの構文を理解するようになっています。しかし、それでも、構文を理解できないケースが存在します。そうしたケースを次に示します。
|
![]()
| |||||||||||||||||||||||||||||||||||||||||