                                                更新日付: 2003 年 3 月 1 日

 Sun[tm] ONE Studio 8: C++ FAQ

     目次

       A. バージョン、パッチ、およびサポート

       B. コンパイラの互換性

       C. コーディングおよび診断

       D. ライブラリの互換性

       E. コンパイル時のパフォーマンスの向上

       F. 実行時のパフォーマンスの向上

     A. バージョン、パッチ、およびサポート

       1. 「標準」および「旧式」入出力ストリームの違いはなんですか ? ま
          た、この問題に関する参考文献を教えてください。

       2. どの C++ コンパイラに互換性があるのか、見分ける方法を教えてく
          ださい。

       3. Sun Open Net Environment (ONE) Studio 8 で使用できるライブラ
          リとして「認定」される RogueWave ライブラリを教えてください。

       4. どのようなパッチがあって、現在のパッチでどのような問題が解決
          されるのかを調べるには、どうすればよいでしょうか ?

       5. libC.so.5 および libCrun.so.1 に対するパッチは必要でしょうか
          ?


                      ---------------------------------

       1. 標準入出力ストリームと旧式入出力ストリームの違いはなんですか
          ?また、この問題に関する参考文献を教えてください。

          2 つのライブラリの設定と実装がまったく異なります。単純な入出
          力のプログラミングインタフェースは酷似しています。 しかし、ユ
          ーザー独自のストリームクラスやマニピュレータの書き込みなど、
          複雑な動作については大きく異なります。

          本バージョンの「旧式」入出力ストリームライブラリは、C++ 3.x
          および 4.x に付属するバージョンと互換性があります。コンパイラ
          に付属するマニュアルのほかにも、次のような参考文献がありま
          す。

             o Steve Teale
               C++ IOStreams Handbook
               Addison-Wesley 1993

          標準入出力ストリームライブラリについては、C++ Standard で説明
          されています。その他にも、次のような参考文献があります。

             o Nicolai Josuttis
               The C++ Standard Library
               Addison-Wesley 1999
               (A tutorial on the entire C++ standard library)

             o Angelika Langer および Klaus Kreft
               Standard C++ IOStreams and Locales
               Addison-Wesley 1999
               (入出力ストリームとロケールに関するチュートリアル)

          単純な入出力を実装するソースコードは、どちらの入出力ストリー
          ムも似通っています。簡単に移行するために、規格外のヘッダー
          <iostream.h>、<fstream.h>、および <strstream.h> が標準の入出
          力ストリームに装備されています。これらのヘッダーは、旧式の入
          出力ストリームにあるグローバルネームスペースと酷似した宣言セ
          ットを提供します。

          たとえば、次のコードは、Sun のコンパイラを使用して旧式と標準
          の両方の入出力ストリームで機能します。ただし、一部のコンパイ
          ラは使用されません。

          #include <iostream.h>

          class myclass {
          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 を使用した単純な入出力
              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;}

       2. C++ コンパイラのバージョン間の互換性はどのようになっています
          か ?

          まず、定義しておきます。「上方互換」とは、先行バージョンのコ
          ンパイラでコンパイルされたオブジェクトコードが後続バージョン
          のコンパイラでコンパイルされたコードとリンクできることを意味
          します。この場合、最終リンクで最新のコンパイラを使用する必要
          があります。

          C++ 4.0、4.1、および 4.2 コンパイラは上方互換性があります。
          (C++ 4.2 マニュアルに登場するコンパイラのバージョン間に「名前
          の符号化」問題があります)。

          互換モード (-compat) に入っている C++ 5.0、5.1、5.2、5.3、
          5.4、および 5.5 コンパイラは、4.2 コンパイラと上位互換性があ
          ります。C++ 4.2 と、バージョン 5.0、5.1、5.2、5.3、5.4、およ
          び 5.5 で生成された実際のオブジェクトコードとは完全に互換性が
          ありますが、後続コンパイラが発するデバッグ情報 (スタブ) は先
          行デバッガと互換性がありません。

          デフォルト標準モードの C++ 5.0、5.1、5.2、5.3、5.4、および
          5.5 コンパイラは上方互換性があります。実際のオブジェクトコー
          ドは完全に互換性がありますが、後続コンパイラの発するデバッグ
          情報 (スタブ) は先行デバッガと互換性がありません。

       3. Sun Open Network Environment (ONE) Studio 8 で使用できるライ
          ブラリとして「認定」される RogueWave ライブラリを教えてくださ
          い。

          サンのコンパイラの各バージョン用に、どのベンダーがどの製品を
          保証しているのかを確実に追跡することはできません。そして、こ
          の FAQ を常に最新に維持するのも、依然として難しい状態です。
          C++ コンパイラの特定バージョンについてベンダーが製品テストを
          行なったかどうかについては、そのベンダーに問い合わせる必要が
          あります。

          ただし、サンのコンパイラに付属している一部の RogueWave ライブ
          ラリについては、出荷バージョンと互換性があることを暗黙に保証
          します。

       4. どのようなパッチがあって、現在のパッチでどのような問題が解決
          されるのかを調べるには、どうすればよいでしょうか ?

          製品のパッチに関する最新情報については、ときどき Hot News の
          Web ページにアクセスしてみてください。
          ( http://sun.co.jp/software/sundev/suncc/hotnews.html) にアクセ
          スしてみてください。

          製品のパッチは http://sunsolve.sun.com からダウンロードできま
          す。

       5. libC.so.5 および libCrun.so.1 に対するパッチは必要でしょうか
          ?

          コンパイラは、そのリリース日付において最新の SUNWlibC パッチ
          とともに出荷されます。

          一般に、Solaris[tm] オペレーティング環境には、これらのライブ
          ラリの最新版が付属しています。 しかし、しばしば、これらのライ
          ブラリには、バグの修正やパフォーマンスの改良のためのパッチが
          提供されます。そうしたパッチは常に累積されていて、常に後方互
          換であるため、提供されている中でも最新のパッチを利用すること
          を推奨します。次の表は、2002 年 3 月現在の最新のパッチ ID を
          示しています。

          データベースに最新のパッケージがあるか確認してください。パッ
          ケージの名前は SUNWlibC (32 ビット) または SUNWlibCx (64 ビッ
          ト) です。

                        表 1:libC および libCrun のパッチ

               パッチ ID           Solaris           アーキテクチャ
                          オペレーティングシステム

               106327-18              7              SPARC (R);/v8

               106300-19              7              SPARC/v9

               106328-17              7              i386

               108434-11              8              SPARC/v8

               108435-11              8              SPARC/v9

               108436-10              8              i386

               111711-04              9              SPARC/v8

               111712-04              9              SPARC/v9

               111713-02              9              i386

     B. コンパイラの互換性

       1. 互換モード (-compat) のコードと標準モードのコードを混在させる
          ことはできますか ?

       2. C++ または C プログラムと F77、F90、または F95 プログラムを組
          み合わせるには、どうすればよいでしょうか ?


                      ---------------------------------

       1. 互換モード (-compat) と標準モードのコードを混在させることはで
          きますか ?

          サンでは混在を推奨しません。「プラグイン」や動的読み込みライ
          ブラリであっても、次の理由から、同じプログラム内のコードの混
          在はサポートしていません。

             o クラスオブジェクトの配置が異なる。

             o 関数の呼び出し処理が異なる。

             o 「名前の符号化」処理が異なる。

             o 例外処理方法が矛盾する。

             o 2 つの入出力ストリームオブジェクトを同じファイル記述子に
               接続すると問題が発生する。

          プログラムの 2 つの部分 (互換モードと標準モード) が通信しない
          ときでも、コード内で例外が送出(スロー)されると、プログラムが
          すぐにクラッシュする可能性があります。

          状況によっては、互換モードと標準モードのオブジェクトファイル
          をまとめてリンクできます。この問題については、コンパイラに付
          属している『C++ 移行ガイド』で詳しく説明されています。 第 1
          章「新旧バイナリの混在」を参照してください。このガイドは、
          http://docs.sun.com からオンラインで入手できます。

       2. C++ または C プログラムと F77、F90、F95 プログラムを組み合わ
          せるには、どうすればよいでしょうか ?

          Workshop 6 update 1 (コンパイラのバージョンは 5.2) 以降、
          -xlang={f90|f95|f77} オプションを使用できるようになりました。
          このオプションは、リンク行に必要なライブラリとそれらのライブ
          ラリの必要な出現順を正確に割り出すようドライバに指示します。

          この -xlang オプションは、C コンパイラには使用できません。 C
          ルーチンと Fortran ルーチンを組み合わせるには、cc でコンパイ
          ルし、Fortran リンカーでリンクする必要があります。

     C. コーディングおよび診断

       1. 標準例外クラスについてあいまいなレポートが提示されるのはなぜ
          ですか ?

       2. C++ 5.3 で派生仮想関数のスロー指定に関してエラーが表示される
          のはなぜですか ?

       3. 自分のプログラムをリンクするとテンプレートインスタンスが失わ
          れるのはなぜですか ? インスタンスはテンプレートキャッシュ内に
          あるようです。

       4. +w2 を使用しているとき、または +w2 +d を使用していないとき
          に、関数は展開されないという警告メッセージが表示されるのはな
          ぜですか ?

       5. -ptr オプションを使って、複数のテンプレートリポジトリを使用し
          たり、複数のプロジェクト間でリポジトリを共有したりできますか
          ? できない場合は、どのようにすればよいでしょうか ?

       6. 次のようなメッセージが表示されることがあります。 SunWS_cache:
          情報: データベースがロックされています。待機中です...」これは
          どういう意味ですか ? 原因はなんですか ? 何か処置が必要ですか
          ? どうすればこのメッセージを出力しないようにできますか ?

       7. printf("%s",NULL) がなぜセグメント例外の原因になるのですか ?

       8. sqrt() の呼び出し方法によって、複素数の平方根の虚部の符号が異
          なります。これはなぜですか ?

       9. クラステンプレート中のフレンド関数はインスタンス化されず、リ
          ンク時にエラーになります。C++ 5.0 ではこのようなことはありま
          せんでした。今回のバージョンでエラーになるのはなぜですか ?

      10. 入れ子になったクラスから包含するクラスのメンバーにアクセスで
          きない、というのはなぜですか ?

      11. 実行時に「pure virtual function call (純粋仮想関数呼び出し)」
          メッセージが表示される原因は何ですか ?

      12. 派生クラスの仮想関数は、識別形式の異なる基底クラスの仮想関数
          を隠す、というのはなぜですか ? 他のコンパイラの場合、このコー
          ドには何も問題がありません。


                      ---------------------------------

       1. コンパイラから標準の例外クラスについてのあいまいさが報告され
          るのはなぜですか ?

          Solaris では、標準ヘッダーの に、標準の 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 指令を比較すると、名前解決は微妙に異なり
          ます。そのため、エラーメッセージは完全に一致しません。

          解決策：

            1. <math.h> の代わりに <cmath> を使用してください。Solaris
               の <cmath> には、C および C++ 規格で指定されている宣言し
               か含まれません。<math.h> の UNIX 固有の機能が必要である
               場合、この解決策は使用できません。

            2. <math.h> も使用する場合は、 using std::exception; と記述
               しないでください。 明示的に std::exception を記述する
               か、typedef を使用して標準例外クラスにアクセスしてくださ
               い。次に例を示します。

               #include <math.h>
               #include <exception>
               std::exception E; // OK
               typedef std::exception stdException; // OK
               stdException F; // OK

            3. using namespace std; を記述しないでください。
               C++ ネームスペース std には非常に多くの名前が含まれるた
               め、実際のコードでこの指令を使用すると、アプリケーション
               コードまたはサードパーティのライブラリと衝突する可能性が
               あります。(C++ プログラミングに関する書籍や記事では、こ
               の using 指令を持たせて小さなサンプルプログラムをさらに
               単純化していることがあります)。個々の using 宣言か、明示
               的な修飾名を使用します。

       2. C++ 5.3 で派生仮想関数のスロー指定に関してエラーが出されるの
          はなぜですか ?

          5.3 C++ コンパイラが新たに強制する C++ 規則では、派生クラスの
          仮想関数は、上書きされる関数が許容する例外のみを許容できま
          す。上書き関数の制限を強化することはできますが、制限を緩める
          ことはできません。次の例で考えてみてください。

          class Base {
          public:
                  // int 型の例外はスローできるが、それ以外はスローできない
                  virtual void f() throw(int);
          };
          class Der1 : public Base {
          public:
                  virtual void f() throw(int); // ok, same specification
          };
          class Der2 : public Base {
          public:
                  virtual void f() throw(); // ok, more restrictive
          };
          class Der3 : public Base {
          public:
                  virtual void f() throw(int, long); // error, can't allow long
          };
          class Der4 : public Base {
          public:
                  virtual void f() throw(char*); // error, can't allow char*
          };
          class Der5 : public Base {
          public:
                  virtual void f(); // error, allows any exception
          };

          次のコードは、C++ 規則の施行理由を示しています。

          #include "base.h" // Base クラスを宣言
          void foo(Base* bp) throw()
          {
              try {
                 bp->f();
              }
              catch(int) {
              }
          }

          Base::f() が int 例外のみをスローするように宣言されているた
          め、関数 foo は、int 例外の捕捉が可能であり、例外のエスケープ
          を許容しないことを宣言します。誰かが後にクラス Der5 を宣言し
          た場合を考えてみましょう。ここでの宣言で、上書き関数は任意の
          例外をスローし、Der5 ポインタを foo に渡すことができます。関
          数 foo のコンパイル時に可視コードに問題がない場合でも、関数
          foo は無効になります。

       3. プログラムをリンクするとテンプレートインスタンスが失われるの
          はなぜですか ? インスタンスはテンプレートキャッシュ内にあるよ
          うです。


          テンプレートキャッシュは、コンパイラが生成するオブジェクトフ
          ァイル間の依存関係リストを保有し、テンプレートインスタンスは
          キャッシュに含まれています。オブジェクトファイルを移動または
          名前変更するか、オブジェクトファイルをライブラリに結合した場
          合、キャッシュへの接続が失われます。2 つの代替手段を次に示し
          ます。

             o オブジェクトファイルを直接的に最終ディレクトリに生成しま
               す。テンプレートキャッシュも同じディレクトリに配置されま
               す。

               次の例は使用しないでください。

               CC -c f1.cc
               mv f1.o /new/location/for/files

               代わりに次の例を使用してください。

               CC -c f1.cc -o /new/location/for/files/f1.o

               makefile マクロでプロセスをカプセル化できます。

             o CC -xar を使用すると、中間的なアーカイブファイル (.a) を
               作成できます。 それぞれのアーカイブには、アーカイブ内の
               オブジェクトが使用するすべてのテンプレートインスタンスが
               含まれます。これらのアーカイブを最終プログラムにリンクし
               ます。一部のテンプレートインスタンスは異なるアーカイブで
               重複しますが、同一アーカイブで重複しないようにリンカーが
               処理します。

               example% CC -c f1.cc f2.cc f3.cc
               example% CC -xar f1.o f2.o f3.o -o temp1.a
               example% CC -c f4.cc f5.cc f6.cc
               example% CC -xar f4.o f5.0 f6.0 -o temp2.a
               example% CC -c main.cc
               example% CC main.o temp1.a temp2.a -o main

       4. +w2 を使用しているとき、または +w2 +d を使用していないとき
          に、関数は展開されないという警告メッセージが表示されるのはな
          ぜですか ?

          C++ コンパイラには、次の 2 種類のインライン化があります。パー
          サーによって行われる C++ の inline 関数のインライン化と、コー
          ドジェネレータによって行われる最適化のインライン化です。C お
          よび Fortran コンパイラには、最適化のインライン化だけしかあり
          ません(1 つのプラットフォームでは、すべてのコンパイラに対して
          同じコードジェネレータが使用されます)。

          C++ コンパイラのパーサーは、暗黙にあるいは明示的に inline と
          して宣言されたすべての関数のインライン化を展開しようと試みま
          す。関数が大きすぎると、パーサーは、+w2 オプションを使用して
          いる場合にだけ警告を発します。+d オプションは、パーサーが関数
          をインライン化しないようにします。そのため、+d を使用すると警
          告メッセージが表示されません。 (また、-g オプションを指定して
          も、C++ インライン関数はインライン化されません)。-xO オプショ
          ンは、このタイプのインライン化には何も影響を与えません。

          最適化のインライン化は、プログラム言語に左右されません。-xO4
          またはそれより高い最適化レベルを選択すると、コードジェネレー
          タは、関数がソースコード内でどのように宣言されていようとも、
          すべての関数を検査して、置き換える利点があれば、関数呼び出し
          をインラインコードで置き換えます。最適化のインライン化 (ある
          いは、関数のインライン化の失敗) に関するメッセージは何も表示
          されません。+d オプションは、最適化のインライン化には何も影響
          を与えません。

       5. -ptr オプションを使って、複数のテンプレートリポジトリを使用し
          たり、複数のプロジェクト間でリポジトリを共有したりできますか
          ? できない場合は、どのようにすればよいでしょうか ?

          バージョン 5.0、5.1、5.2、5.3、5.4、および 5.5 では、-ptr オ
          プションはサポートされていません。バージョン 4.2 で提供されて
          いましたが、必ずしもユーザーの期待通りに機能せず、多くの問題
          を発生させていました。

          最良のアドバイスは、複数のプロジェクト間でリポジトリを共有し
          ないことです。リポジトリを共有すると、解決を図れないほど深刻
          な問題が発生する可能性があります。1 つのプロジェクトは 1 つの
          ディレクトリだけでコンパイルしてください。別のプロジェクトの
          バイナリには別のディレクトリを使用してください。

          バージョン 5.0 以降、コンパイラは、生成するオブジェクトファイ
          ルと同じディレクトリにテンプレートリポジトリを格納します。1
          つのプロジェクトに複数のリポジトリを使用する場合は、関係する
          リポジトリを格納するディレクトリにオブジェクトファイルを作成
          してください。リンク時、それらオブジェクトファイルに関係する
          リポジトリに対して、自動的にテンプレートインスタンスの検索が
          行われます。コンパイラオプションは必要ありません。

       6. 次のようなメッセージが表示されることがあります。SunWS_cache:
          情報: データベースがロックされています。待機中です...」これは
          どういう意味ですか ? 原因はなんですか ? どうすればこのメッセ
          ージを出さないようにできますか ?

          テンプレートを使用するプログラムをコンパイルするときに、
          SunWS_cache/CC_state 内のコンパイル状態情報の更新が必要な場
          合、コンパイラは、必ず SunWS_cache 出力ディレクトリをロックし
          ます。テンプレートを使用する複数のプロセスが同じディレクトリ
          でコンパイルしようとする場合には、一度に 1 つのプロセスだけが
          ロックを取得します。

          バージョン 4.2、5.0、および 5.1 の C++ コンパイラは、ロック取
          得待ちのプロセスがあるときは必ず、「SunWS_cache: 情報: データ
          ベースがロックされています。待機中です...」というメッセージを
          出力します。このメッセージは単に情報を通知するためのものです
          から、無視してください。このメッセージは、ある別のコンパイル
          プロセスがデータベースをロックしたということを意味していま
          す。 その別のジョブが終了すると、現在のジョブが続行されます。
          dmake を使用しているために、このメッセージが出ると考えられま
          す。このメッセージを出さないようにすることはできません。最新
          の C++ 5.2 パッチ、C++ 5.3 コンパイラ、5.4 コンパイラ、および
          5.5 コンパイラでは、別のロック方式が採用されており、このメッ
          セージは出ません。

       7. printf("%s",NULL) がなぜセグメント例外の原因になるのですか ?

          アプリケーションの中には、NULL 文字ポインタは空文字列へのポイ
          ンタと同じ扱いをしなければならないと誤って認識しているものが
          あります。これらのアプリケーションでは、NULL 文字ポインタがア
          クセスされるとセグメント違反が発生します。

          いくつかの理由により、 *printf() の関数ファミリには NULL ポイ
          ンタのチェック機能がありません。これだけに限りませんが、次の
          ようなことが挙げられます。

             o チェック機能を待たせると、誤った安心感を与える。プログラ
               マに、printf() への NULL ポインタ引き渡しがうまくいった
               と思い込ませてしまいます。

             o プログラマが移植性のないコードを書くことを助長する。ANSI
               C、XPG3、XPG4、SVID2、および SVID3 では、 printf("%s",
               pointer) には NULL で終わる文字の配列への pointer ポイン
               トがなければならないと規定されています。

             o デバッグが難しくなる。プログラマが NULL ポインタを
               printf() に渡してからプログラムがコアに入るのであれば、
               デバッガを使用すれば、不正なポインタを指定した printf()
               呼び出しを簡単に見つけ出すことができます。しかし、
               printf() が "(NULL ポインタ)" と出力することにより、その
               バグを非表示にした場合、その後で進行するほかのプログラム
               は、ある実際のデータが欲しいときに、"(NULL ポインタ)" を
               解釈しようとするでしょう。その時点では、実際の問題がどこ
               に隠れているか判断することは不可能かもしれません。

          NULL ポインタを *printf に渡すアプリケーションがある場合は、
          ロケーション 0 に値 0 を設定するためのメカニズムを提供する特
          定の共有オブジェクト /usr/lib/0@0.so.1 を使用することができま
          す。このライブラリは、NULL ポインタの型による違いに関連するす
          べてのエラーをマスクするので、このライブラリは、コードを修正
          するまでの一時的な回避策としての使用に限定してください。

       8. sqrt() の呼び出し方法によって、複素数の平方根の虚部の符号が異
          なります。これはなぜですか ?

          この関数の実装は、「C99 csqrt Annex G 仕様」で規定されていま
          す。たとえば、次のコード例の出力を見てください。

          complex sqrt (3.87267e-17, 0.632456)
          float sqrt (3.87267e-17, -0.632456)

             o 互換モードで libcomplex を使用した例:

               #include <iostream.h>
               #include <math.h>
               #include <complex.h>

               int main ()
               {
                     complex ctemp(-0.4,0.0);
                     complex c1(1.0,0.0);
                     double  dtemp(-0.4);
                     cout<< "complex sqrt "<< sqrt(ctemp)<<endl;
                     cout<< "float sqrt   "<< sqrt(c1*dtemp)<<endl;
               }

             o 標準モードで libCstd を使用した例:

               #include <iostream>
               #include <math.h>
               #include <complex>

               using namespace std;

               int main ()
               {
                    complex<double> ctemp(-0.4,0.0);
                    complex<double> c1(1.0,0.0);
                    double  dtemp(-0.4);
                    cout<< "complex sqrt "<< sqrt(ctemp)<<endl;
                    cout<< "float sqrt   "<< sqrt(c1*dtemp)<<endl;
               }

             o complex の sqrt 関数は、atan2 を使用して実装されます。次
               の例は、atan2 を使用することによって起こる問題を示したも
               のです。このプログラムの出力は以下のとおりです。

               c=-0.000000  b=-0.400000  atan2(c, b)=-3.141593
               a=0.000000  b=-0.400000  atan2(a, b)=3.141593

               1 つめの例では、atan2 の出力は負の数になり、2 つめの例で
               は正の数です。これは、最初の引数として -0.0 または 0.0
               のどちらが渡されるかによります。

               #include <stdio.h>
               #include <math.h>

               int main()
               {
                   double a = 0.0;
                   double b = -0.4;
                   double c = a*b;
                   double d = atan2(c, b);
                   double e = atan2(a, b);
                   printf("c=%f  b=%f  atan2(c, b)=%f\n", c, b, d);
                   printf("a=%f  b=%f  atan2(a, b)=%f\n", a, b, e);
               }

       9. クラステンプレート中のフレンド関数はインスタンス化されず、リ
          ンク時にエラーになります。C++ 5.0 ではこのようなことはありま
          せんでした。今回のバージョンでエラーになるのはなぜですか ?

          次のテストケースは、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

          Undefined                       first referenced
          symbol                              in file
          std::basic_ostream<char,std::char_traits<char>
          >&operator<<(std::basic_ostream<char,std::char_traits<char> >&,const
          TList<int>&) 4421826.o

          ld: fatal: Symbol referencing errors. No output written to a.out

          規格によると、このテストケースは無効です。 問題点は以下の宣言
          にあります。

          friend ostream& operator<< (ostream&, const TList&);

          上の宣言は、いずれのテンプレートインスタンスも参照していませ
          ん。

          非修飾名の参照は、フレンド宣言の 1 箇所で認識可能であるとして
          も、テンプレート宣言と一致しません。フレンド宣言をテンプレー
          トと一致させるには、フレンド宣言をテンプレート関数として宣言
          するか、または名前を修飾する必要があります。

          どちらの方法でも、テンプレート用の宣言は、フレンド宣言の 1 箇
          所で認識可能である必要があります。

          つまり、フレンド宣言はテンプレートを参照しませんが、関数呼び
          出しにもっとも一致する関数を宣言します。 (ほかの点で等しいの
          であれば、テンプレート関数よりもテンプレートでない関数の方が
          のぞましいと言えます)。

          次のコードは有効です。

          template <class T> class TList;
          // これで、operator<< テンプレートを宣言できる

          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&);
          };

      10. 入れ子になったクラスから包含するクラスのメンバーにアクセスで
          きない、というのはなぜですか ?

          class Outer {
              typedef int my_int;
              static int k;
              class Inner {
                  my_int j;          // エラー。my_int にはアクセスできない
                  int foo() {
                          return k; // エラー。k にはアクセスできない
                          }
                  };
          };

          ARM および C++ 標準によると、入れ子になったクラスは、包含する
          クラスのメンバーに特別にアクセスすることはありません。my_int
          および k は Outer 内で非公開なので、Outer のフレンドだけがメ
          ンバーにアクセスできます。入れ子になったクラスをフレンドにす
          るためには、クラスを前もって宣言してからフレンドにする必要が
          あります。以下に例を示します。

          class Outer {
              typedef int my_int;
              static int k;

                  // 次の 2 行をクラス定義の前に追加
              class Inner;
              friend class Inner;

              class Inner {
                  my_int j;         // OK
                  int foo() {
                          return k; // OK
                          }
                  };
          };

      11. 実行時に「pure virtual function call (純粋仮想関数呼び出し)」
          メッセージが表示される原因は何ですか ?

          プログラム内に、あるエラーが発生すると必ず、「pure virtual
          function call (純粋関数呼び出し)」メッセージが表示されます。
          このエラーは、次の 2 つのどちらかの場合に起こります。

             o このエラーは、抽象クラスのコンストラクタまたはデストラク
               タから、外部関数に「this」パラメータを渡したことによって
               発生します。 構築および破壊時に、「this」は、コンストラ
               クタまたはデストラクタ自身のクラスの型を持ち、最終的に構
               築されるクラスの型は持ちません。そこで純粋仮想関数の呼び
               出しを終えることができます。次の例で考えてみてください。

               class Abstract;

               void f(Abstract*);

               class Abstract {
               public:
                       virtual void m() = 0; // 純粋仮想関数
                       Abstract() { f(this); }   // コンストラクタが "this" を渡す
               };

               void f(Abstract* p)
               {
                       p->m();
               }

               「Abstract」コンストラクタから f が呼び出されると、
               「this」は「Abstract*」型となり、関数 f は純粋仮想関数 m
               を呼び出そうとします。

             o 明示的な修飾子指定を行わずに定義した純粋仮想関数を呼び出
               そうとした場合にも、このエラーが起こることがあります。本
               体に純粋仮想関数を指定することはできますが、呼び出し時に
               は修飾名を使用し、仮想呼び出しメカニズムをバイパスした場
               合のみ、呼び出すことができます。

               class Abstract {
               public:
                       virtual void m() = 0; // 本体は後で提供される
                       void g();
               };

               void Abstract::m() { ... } // m の定義

               void Abstract::g()
               {
                       m(); // エラー。純粋仮想の m を呼び出そうとしている
                       Abstract::m(); // OK。呼び出しは完全に修飾されている
               }

      12. 派生クラスの仮想関数は、識別形式の異なる基底クラスの仮想関数
          を隠す、というのはなぜですか ? 他のコンパイラの場合、このコー
          ドには何も問題がありません。

          C++ の規則では、多重定義はスコープ内でのみ認められ、スコープ
          を超えるものは認められません。基底クラスは、派生クラスのスコ
          ープを取り巻くスコープ内にあると考えられます。そのため、派生
          クラス内で宣言された名前は、基底クラス内のあらゆる関数を隠
          し、多重定義できません。この基本的な C++ 規則は、ARM よりも旧
          式です。

          ほかのコンパイラが、何も異常を表示しないとしても、これにより
          損害を被ります。なぜなら、コードは、期待するとおりには動作し
          ないからです。サンのコンパイラは、そのコードを受け付けるとき
          に警告を表示します。(正当なコードですが、おそらく期待どおりに
          は動作しません。)

          多重定義されたセットに基底クラス関数を含めたい場合は、基底ク
          ラス関数を現在のスコープに入れるために手を加える必要がありま
          す。デフォルトの標準モードでコンパイルを行っている場合は、宣
          言の使用を追加できます。

          class Base {
          public:
                  virtual int    foo(int);
                  virtual double foo(double);
          };

          class Derived : public Base {
          public:
                  using Base::foo; // 基底クラス関数を多重定義セットに追加
                  virtual double foo(double); // 基底クラスバージョンを置き
                                                 換える
          };

     D. ライブラリの互換性

       1. 完全準拠の C++ 標準ライブラリ (stdlib) の入手方法を教えてくだ
          さい。あるいは、現在の libCstd がサポートしていない機能には、
          どのようなものがありますか ?

       2. C++ 標準のテンプレートライブラリ (STL) が必要です。 どこで入
          手できるのでしょうか ? 互換モード (-compat) 用のものはあるの
          でしょうか ?

       3. libCstd で失われた標準ライブラリ機能にはどのようなものがあり
          ますか ?

       4. 標準ライブラリの機能が失われることで、どのような影響があるの
          でしょうか ?

       5. 標準ストリームで機能する tools7 ライブラリバージョンはありま
          すか ? あるいは tools8 はまもなく入手できるようになるのでしょ
          うか ?


                      ---------------------------------

       1. 完全準拠の C++ 標準ライブラリ (stdlib) の入手方法を教えてくだ
          さい。あるいは、現在の libCstd がサポートしていない機能には、
          どのようなものがありますか ?

          このリリースには、STLport の Standard Library 実装のバージョ
          ン 4.5.3 がオプションの標準ライブラリとして含まれています。
          STLport は C++ 規格に厳密に準拠していながら、一般的な拡張機能
          も保持しています。ただし、デフォルトで使用される標準ライブラ
          リとのバイナリ互換性はありません。

          現在の libCstd は、バージョン 5.0 の C++ コンパイラ用に開発さ
          れました。このバージョンは、クラスのメンバーとしてテンプレー
          トをサポートしていません。標準ライブラリの一部には、メンバー
          テンプレートが必要であり、このことは一部機能が失われることを
          意味します。これは特に、暗黙の型変換を可能にするコンストラク
          タテンプレートを持つコンテナクラスにおいて起こります。その場
          合は、回避策としてソースコード内に明示的な型変換を記述する必
          要があります。

          バージョン 5.1 以降、C++ コンパイラはクラスのメンバーとしてテ
          ンプレートをサポートしていて、規格に準拠したライブラリを使用
          できます。ソースとバイナリレベルの互換性を損なうことなくライ
          ブラリを更新することはできないため、サンでは、同じ制限がある
          libCstd を引き続き出荷しています。

          gnu と SGI の Web サイトでは、パブリック版の標準ライブラリが
          配布されています。また、RogueWave、Dinkumware などのベンダー
          からライブラリを購入することもできます。STL については、次の
          質問を参照してください。

       2. C++ 標準のテンプレートライブラリ (STL) が必要です。どこで入手
          できるのでしょうか 互換モード (-compat) 用のものはあるのでし
          ょうか ?

          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++ 標準ライブラリの置き換え」を参照し
          てください。このガイドは、http://docs.sun.com からオンライン
          で入手できます。

          このリリースでは、-compat モードに対応した標準ライブラリ機能
          は提供していません。サードパーティのライブラリ (LTLport など)
          を -compat モード用に構成できる場合もありますが、このリリース
          はそのような構成をサポートしません。

       3. libCstd で失われた標準ライブラリ機能にはどのようなものがあり
          ますか ?

          標準ライブラリは、元は (C++ 5.0 では) コンパイラ中にメンバー
          テンプレートおよび部分の特殊化を必要とする機能をサポートせず
          に構築されたものです。これらの機能は C++ 5.1 以降、使用可能で
          すが、標準ライブラリ中にオンにすることはできません。これらは
          下方互換性を残しているからです。下記は、各機能で無効にされ、
          その機能性が失われた機能リストです。

             o 無効にされた機能: メンバーテンプレート関数

                  + <complex> 内の complex クラス:

                    template <class X> complex<T>& operator= (const
                    complex<X>& rhs)
                    template <class X> complex<T>& operator+= (const
                    complex<X>& rhs)
                    template <class X> complex<T>& operator-= (const
                    complex<X>& rhs)
                    template <class X> complex<T>& operator*= (const
                    complex<X>& rhs)
                    template <class X> complex<T>& operator/= (const
                    complex<X>&)

                  + <utility> 内の pair クラス:

                    template<class U, class V> pair(const pair<U, V>
                    &p);

                  + <locale> 内の locale クラス:

                    template <class Facet> locale combine(const locale&
                    other);

                  + <memory> 内の auto_Ptr クラス:

                    auto_ptr(auto_ptr<Y>&);
                    auto_ptr<Y>& operator =(auto_ptr<Y>&);
                    template <class Y> operator auto_ptr_ref<Y>();
                    template <class Y> operator auto_ptr<Y>();

                  + <list> 内の list クラス:

                    メンバーテンプレートのソート

                  + ほとんどのテンプレートクラス:

                    テンプレートコンストラクタ

             o 無効にされた機能: メンバーテンプレートクラス

               <memory> 内の auto_ptr クラス:

               template <class Y> class auto_ptr_ref{};
               auto_ptr(auto_ptr(ref<X>&);

             o 無効にされた機能: 部分的に特殊化されている関数テンプレー
               トの引数の多重定義

               <deque>、<map>、<set>、<string>、<vector> および <
               iterator> では、次のテンプレート関数 (非メンバー) はサポ
               ートされません。

                  + map、multimap、 set、multiset、basic_string、
                    vector、reverse_iterator、および istream_iterator
                    クラスの場合:

                    bool operator!= ()

                  + map、multimap、 set、multiset、basic_string、
                    vector、および reverse_iterator クラスの場合:

                    bool operator> ()
                    bool operator>= ()
                    bool operator<= ()

                  + map、multimap、 set、multiset、basic_string、および
                    vector クラスの場合:

                    void swap()

             o 無効にされた機能: デフォルトのパラメータを使ったテンプレ
               ートクラスの部分特殊化

          <algorithm> では、次のテンプレート関数 (非メンバー) はサポー
          トされません。

          count(), count_if()

          <iterator> では、次のテンプレートはサポートされません。

          template <class Iterator> struct iterator_traits {}
          template <class T> struct iterator_traits<T*> {}
          template <class T> struct iterator_traits<const T*> {}

       4. 標準ライブラリの機能が失われることで、どのような影響があるの
          でしょうか ?

          C++ 規格では正当なコードがコンパイルされないことがあります。

          もっともよくあるのは、ペアの第 1 要素は const であるのに、そ
          のようには宣言されていないマップを作成している場合です。 メン
          バーコンストラクタテンプレートは、必要に応じ暗黙で pair<T, U>
          を pair<const T, U> に変換します。しかし、コンストラクタがな
          いために、変換されずに、コンパイルエラーになります。

          マップ内のペアの第 1 要素を変更することはできないため、もっと
          も簡単な解決策は、ペア型を作成するときに明示的な const を使用
          することです。たとえば、pair<int, T> ではなく pair<const int,
          T> を使用します。 また、map<int, T> ではなく map<const int,
          T> を使用します。

       5. 標準ストリームで機能する tools7 ライブラリバージョンはありま
          すか ? あるいは tools8 はまもなく入手できるようになるのでしょ
          うか ?

          はい、あります。ただし、C++ 5.3、5.4、および 5.5 のみです。
          -library=rwtools7_std コマンドを使用してこのライブラリとリン
          クしてください。

          RogueWave は Tools.h++ の機能を変更しており、現在では
          SourcePro 製品の一部としてのみ提供しています。そのような理由
          から、Tools.h++ version 8 は存在しません。

     E. コンパイル時のパフォーマンスの向上

       1. バージョン 4.2 と比べて、バージョン 5.0 および 5.1 のコンパイ
          ラのコンパイル時間は大幅に長くなっています。将来、この問題は
          解決されるのでしょうか ?

       2. バージョン 4.2 のコンパイラと比べてバイナリのサイズがかなり大
          きくなります。この問題の解決策はあるのでしょうか ?

       3. 1 つのコンパイルプロセスを複数のプロセッサに分散できるでしょ
          うか ? 一般的に、マルチプロセッサ (MP) システムの方がコンパイ
          ル時のパフォーマンスは常に良いのでしょうか ?


                      ---------------------------------

       1. バージョン 4.2 と比べて、バージョン 5.0 および 5.1 のコンパイ
          ラのコンパイル時間は大幅に長くなっています。将来、この問題は
          解決されるのでしょうか ?

          5.1 のパッチ 01、バージョン 5.2、5.3、5.4、および 5.5 で大幅
          にコンパイル時間を改善しました。コンパイラのパフォーマンスに
          満足できない場合、次の推奨事項に留意してください。

            a. 極端な場合、インライン化は膨大な時間がかかる原因になりま
               す。-xO4 または -xO5 のいずれかのオプションを使用する
               と、コードジェネレータがいくつかの関数を自動的にインライ
               ン化します。-xO3 のような低い最適化レベルを使用する必要
               があるかもしれません。オプティマイザが特定関数を自動的に
               インライン化しないようにするには、-xinline オプションを
               使用できます。

            b. 大きな関数の明示的なインライン化を無効にします。明示的な
               インライン化の詳細については、質問 G.2 を参照してくださ
               い。

            c. デフォルトでは、すべてのインスタンスがオブジェクトの
               SunWS_cache サブディレクトリに書き込まれます。大量のイン
               スタンスがある場合は、リポジトリの管理に最大 95% のコン
               パイル時間が費やされることがあります。テンプレートリポジ
               トリをバイパスするテンプレートインスタンス化モデル (明示
               的、大域、静的、および半明示的) のいずれかを使用可能かど
               うかを調べてください。 特定のモデルを指定するには、
               -instances=model オプションを使用します。 コンパイル時間
               の問題の原因がインスタンス化にある場合は、別のインスタン
               ス化モデルを使用することによって、大幅にコンパイル時間が
               短縮されることがあります。

               これらの代替モデルでは、プログラミングスタイルが制約され
               ることに注意してください。テンプレートインスタンス化モデ
               ルについての詳細は、『C++ ユーザーズガイド』の「テンプレ
               ートのコンパイル」を参照してください。このガイドは、
               http://docs.sun.com からオンラインで入手できます。

       2. バージョン 4.2 のコンパイラと比べてバイナリのサイズがかなり大
          きくなります。この問題の解決策はあるのでしょうか ?

          -g オプションでコンパイルを行うと、バージョン 5.0 からコンパ
          イラがテンプレートのデバッグ用の大量の情報を書き出すため、バ
          イナリのサイズが大きくなります。バージョン 5.1 では、多くの種
          類のプログラムについて、そうしたデバッグ情報のサイズは大幅に
          縮小されます。5.2、5.3、5.4 および 5.5 コンパイラでは、さらに
          パフォーマンスが向上しています。多くの場合、バイナリのサイズ
          の縮小率は 25% から 50% 強になっています。こうした改良は、コ
          ードで名前空間やテンプレート、多数の継承レベルを持つクラス階
          層が使用されている場合に特に顕著です。

       3. 1 つのコンパイルプロセスを複数のプロセッサに分散できるでしょ
          うか ? 一般的に、常にマルチプロセッサ (MP) システムの方がコン
          パイル時のパフォーマンスは常に良いのでしょうか ?

          コンパイラそのものはマルチスレッド化されていません。しかし、
          コンパイラは 1 回のコンパイルで常に多数の他のプロセスを同時に
          動作させるため、MP システムの方がパフォーマンスの向上を期待で
          きます。

          dmake (コンパイラに付属しているツールの 1 つ) を使用すると、
          複数のコンパイルを同時に実行できます。ただし、この同時コンパ
          イルでは、テンプレートキャッシュを使用する必要があるため、キ
          ャッシュへのアクセスの競合がパフォーマンスのネックになること
          があります。この FAQ 集の 4c で説明されているオプションなど、
          キャッシュの問題を回避するためのオプションを調べてください。
          詳細は、コンパイラに付属する『C++ ユーザーズガイド』の「テン
          プレートのコンパイル」を参照してください。このガイドは、
          http://docs.sun.com からオンラインで入手できます。

     F. 実行時のパフォーマンスの向上

       1. C++ は、"inline" キーワードの付いた関数を常にインライン化する
          のでしょうか ? あるいは、そのように記述したとしても、インライ
          ン化されない関数があるのは、どうしてでしょうか ?

       2. stdlib ストリームは、gcc または KAI ストリームより低速です。
          パフォーマンスの低下が大きすぎます。解決策はあるのでしょうか
          ?


                      ---------------------------------
   * C++ は、「inline」キーワードの付いた関数を常にインライン化するので
     しょうか ? あるいは、そのように記述したとしても、インライン化され
     ない関数があるのはどうしてでしょうか ?

     基本的に、コンパイラは、<inline> 宣言を指令とみなし、そのように宣
     言された関数をインライン化しようとします。バージョン 5.1、5.2、
     5.3、5.4、および 5.5 のコンパイラでは、インライン化アルゴリズムが
     改良され、より多くの構文を理解するようになっています。しかし、それ
     でも、構文を理解できないケースが存在します。そうしたケースを次に示
     します。

        * 5.2、5.3、5.4、5.5 C++ コンパイラで開始すると、まれに実行され
          た関数呼び出しが展開されない場合があります。この変更は、コン
          パイル速度、出力コードサイズ、および実行時速度の均衡をとるた
          めに役立ちます。

          たとえば、静的な変数初期化で使用される式は 1 度だけ実行される
          ため、式中の関数呼び出しは展開されません。インライン関数 func
          は、静的変数の初期化式中で呼び出されると、展開されないことが
          あることに注意してください。ほかの場所でインライン化されたま
          まです。 同様に、例外ハンドラ中の関数呼び出しは、これらのコー
          ドにめったに実行されないので、展開されない可能性があります。

        * 再帰関数は、最初の呼び出しレベルに対してのみインライン化され
          ます。 コンパイラは、再帰関数の呼び出しをあいまいにインライン
          化できません。 現在の実装は、インライン化されている任意の関数
          への初回呼び出しの時点で停止します。

        * 時として、小さな関数に対する呼び出しでさえもインライン化され
          ないことがあります。この理由は、展開後の合計サイズが大きすぎ
          るということです。 たとえば、func1 が func2 を呼び出し、func2
          が func3 を呼び出す、というような場合があります。これらの関数
          が小さくて、再帰的な呼び出しはない場合でも、コンパイラがそれ
          らすべてを展開した場合には、展開後のサイズの合計が大きすぎる
          可能性があります。

          多くの標準テンプレート関数は、小さいけれども深い呼び出しチェ
          ーンを持っています。このような場合、2、3 個の呼び出しレベルだ
          けが展開されます。

        * コンパイラは、goto 文、ループ、および try/catch 文を含む C++
          インライン関数をインライン化しません。しかし、-xO4 レベルで
          は、オプティマイザによってインライン化することがあります。

        * 大きな関数はインライン化されません。C++ コンパイラのコンパイ
          ラとオプティマイザはともに、インライン化後の関数のサイズに制
          限があります。この制限はサンとしての一般的な推奨事項です。 特
          別にこのサイズを緩める、または厳しくしたい場合は、内部オプシ
          ョンについて技術サポートに問い合わせてください。

        * 仮想関数がサブクラス内で再定義されていないとしても、インライ
          ン化することはできません。これは、別のコンパイルユニットにサ
          ブクラスと仮想関数の再定義が含まれているのかどうかをコンパイ
          ラが認識できないためです。

     以前の一部のバージョンでは、複雑な if 文と return 文を持つ関数がイ
     ンライン化できませんでした。しかし、この制限はなくなりました。ま
     た、インライン関数に対するデフォルトのサイズ制限も緩和されていま
     す。プログラムによっては、こうした修正によってインライン化可能な関
     数が増えますが、コンパイル時間が長くなり、コードのサイズが大きくな
     ることがあります。

     C++ インライン関数のインライン化を完全に無効にするには、+d オプシ
     ョンを使用します。

     これとは別に、最適化レベルが高い (-xO4) 場合、オプティマイザは、制
     御フローなどの結果に基づいて関数をインライン化します。このインライ
     ン化は自動的で、関数が "inline" 宣言されているかどうかに関係なく行
     われます。

   * stdlib ストリームは、gcc または KAI ストリームよりも低速です。パフ
     ォーマンスの低下が大きすぎます。解決策はあるのでしょうか ?

     C++ 5.4 では、デフォルトのストリームライブラリのパフォーマンスが向
     上しています。問題がある場合には、次の解決策を参考にしてください。

        * -library=iostreams オプションの利用。このオプションは、標準ス
          トリームではなく「従来」の iostreams を使用します。これらのク
          ラスは効率的であることがわかっています。残念なことに、従来の
          iostreams を使用するということは、 stdlib の機能が利用できな
          くなり、-library=iostreams を使って、プログラム全体をコンパイ
          ルする必要があることを意味します。また、従来の iostreams を使
          用するには、一部ソースコードの変更が必要になることがありま
          す。

        * -library=stlport4 および -library=iostreams オプションの指定
          による STLport の利用。標準モード (デフォルト) と互換モード (
          -compat) の両方で、かなりのパフォーマンスが得られるようです
          (gcc の 50% 以内)。

        * RogueWave の stdlib2.2.3 の利用。残念なことに、このライブラリ
          は、バージョン 5.0、5.1、および 5.2 のコンパイラにデフォルト
          で付属しているライブラリの stdlib2.1.1 と互換性がありません。
          そのため、アプリケーションに他のミドルウェアが必要となる場合
          に機能しない可能性があります。

                                                更新日付: 2003 年 3 月 1 日
  ------------------------------------------------------------------------

Copyright © 2003 Sun Microsystems, Inc., All rights reserved. Use is
subject to license terms.
