Innovating@Sun コミュニティ ご購入について (0120-33-9096) マイ・アカウント 日本 [Change] 日本語

FAQs
C++言語システム
      Studio 11
      ライセンス
    C++言語システム
      Sun Performance Library
目次
バージョン、パッチ、およびサポート
  1. 新リリースで C++ コンパイラを確実に識別する方法を教えてください。
  2. 「標準」および「旧式」入出力ストリームの違いはなんですか。また、この問題に関する参考文献を教えてください。
  3. どの C++ コンパイラのバージョンに互換性があるのか、見分ける方法を教えてください。
  4. Sun Studio ソフトウェア用として、どのような RogueWave ライブラリが「保証」されていますか ?
  5. どのようなパッチがあって、現在のパッチでどのような問題が解決されるのか、どうすれば知ることができるのでしょうか ?
  6. libC.so.5 および libCrun.so.1 に対するパッチは必要でしょうか ?
コンパイラの互換性
  1. 最近、Solaris オペレーティングシステムにパッチを適用したところ、コードをコンパイルできなくなりました。何が起こったのでしょうか ?
  2. 互換モード (-compat) のコードと標準モードのコードを混在させることはできますか ?
  3. C++ または C プログラムと F77、F90、または F95 プログラムを組み合わせるには、どうすればよいでしょうか ?
コード作成と診断
  1. foo.cc をコンパイルしていないとき、またはプログラムに foo.cc を組み込んでいないときにファイル foo.cc に関するエラーや警告が発生するのはなぜですか ?
  2. -P 前処理オプションで生成された foo.i ファイルをコンパイルすると、定義が重複するというエラーメッセージが表示されるのはなぜですか ?
  3. SPARC V9 アーカイブライブラリを動的ライブラリにリンクするとエラーが発生するのはなぜですか。Sun Studio 8 では正常に機能しました。
  4. 「SunWS_cache: エラー: SunWS_cache のロックの試みが失敗しました」というメッセージが表示される原因は何ですか ?
  5. リンカーで「ld: warning: symbol 'clog' has differing types (ld: 警告: シンボル 'clog' に異なるタイプがあります)」という警告が発生するのはなぜですか ?
  6. STLport を使用するマルチスレッドプログラムを -xarch=v8plus または -xarch=v8plusa を指定してコンパイルするとクラッシュするのはなぜですか ?
  7. abs() の呼び出しがあいまいである、というのはなぜですか ?
  8. 一時オブジェクトはいつ破棄されるのですか ?
  9. 標準例外クラスについてあいまいさが報告されるのはなぜですか ?
  10. 派生仮想関数でのスロー (throw) 指定について C++ 5.3 がエラーメッセージを表示するのはなぜですか ?
  11. 自分のプログラムをリンクするとテンプレートインスタンスが失われるのはなぜですか。インスタンスはテンプレートキャッシュの中にあるようです。
  12. +w2 を使用しているとき、または +w2 +d を使用していないときに、関数は展開されないという警告メッセージが表示されるのはなぜですか ?
  13. -ptr オプションを使って、複数のテンプレートリポジトリを使用したり、複数のプロジェクト間でリポジトリを共有したりできますか。できない場合は、どのようにすればよいでしょうか ?
  14. fprintf("%s",NULL) がなぜセグメント例外の原因になるのですか ?
  15. sqrt() の呼び出し方法によって、複素数の平方根の虚部の符号が異なります。これはなぜですか ?
  16. クラステンプレート中のフレンド関数はインスタンス化されず、リンク時エラーになります。C++ 5.0 ではこのようなことはありませんでした。 今回のバージョンでエラーになるのはなぜですか ?
  17. 入れ子になったクラスから包含するクラスのメンバーにアクセスできない、というのはなぜですか ?
  18. 実行時に「pure virtual function call (純粋仮想関数呼び出し)」メッセージが表示される原因は何ですか ?
  19. 派生クラスの仮想関数は、識別形式の異なる基底クラスの仮想関数を隠す、というのはなぜですか。ほかのコンパイラの場合、このコードには何も問題がありません。
ライブラリの互換性
  1. C++ コンパイラの実行時ライブラリから別の機能を得るために変更できるライブラリ構成マクロはどれですか ?
  2. -I オプションと -L オプションを使用する必要があるのは、どのようなときですか ?
  3. 完全準拠の C++ 標準ライブラリ (stdlib) の入手方法を教えてください。あるいは、現在の libCstd がサポートしていない機能には、どのようなものがありますか ?
  4. C++ 標準テンプレートライブラリ (STL) が必要です。どこで入手できるのでしょうか。互換モード (-compat) 用のものはあるのでしょうか ?
  5. libCstd で失われた標準ライブラリ機能にはどのようなものがありますか ?
  6. 標準ライブラリの機能が失われることで、どのような影響があるのでしょうか ?
  7. 標準ストリームで機能する tools7 ライブラリのバージョンはありますか。あるいは tools8 はまもなく入手できるようになるのでしょうか ?
コンパイル時のパフォーマンス
  1. 最近 Sun Open Network Environment (Sun ONE) Studio 8 C++ 5.5 コンパイラにアップグレードし、コンパイル中のリンク時間が大幅に増加していることに気付きました。この原因は何でしょうか。また、回避策はあるでしょうか?
  2. C++ コンパイラのプリコンパイル済みヘッダー機能を使って開始してもコンパイル時間が短縮されないのはなぜですか ?
  3. 大きなファイルのコンパイルに要する時間が、小さなファイルよりも大幅に長いのはなぜですか ?
  4. バージョン 4.2 と比べて、バージョン 5.0 および 5.1 のコンパイラのコンパイル時間は大幅に長くなっています。将来、この問題は解決されるのでしょうか ?
  5. バージョン 4.2 のコンパイラと比べてバイナリのサイズがかなり大きくなります。この問題の解決策はあるのでしょうか ?
  6. 1 つのコンパイルプロセスを複数のプロセッサに分散できるでしょうか。一般的に、いつでもマルチプロセッサ (MP) システムの方がコンパイル時のパフォーマンスは良いのでしょうか ?
実行時のパフォーマンス
  1. 標準ライブラリのストリームは、gcc または KAI ストリームより低速です。パフォーマンスの低下が大きすぎます。 解決策はあるのでしょうか ?
  2. C++ は、"inline" キーワードの付いた関数を常にインライン化するのでしょうか ? あるいは、そのように記述したとしても、インライン化されない関数があるのはどうしてでしょうか ?
 
 

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


Q:
新リリースで C++ コンパイラを確実に識別する方法を教えてください。
A:

すべてのコンパイラでは、コンパイラを識別するマクロが事前定義されています。コンパイラのベンダーは、これらの事前定義済みマクロをリリース間で変更しないようにしていますが、Sun は特にこれらのマクロを不変のパブリックインタフェースとして記録しています。

使用しているコンパイラを確認するには、事前定義済みマクロをテストして用途に適した文字列を出力する簡単なプログラムを作成できます。また、擬似プログラムを作成し、-E (または同等のオプション) を指定してコンパイルする方法もあります。

C++ コンパイラの事前定義済みマクロの一覧については、「C++ ユーザーズガイド」の索引で「マクロ」を検索してください。コンパイラを識別するには、3 桁の 16 進数である __SUNPRO_CC の値を確認します。1 桁目はメジャーリリースです。2 桁目はマイナーリリースです。3 桁目はマイクロリリースです。たとえば、C++ 5.6 は 0x560 になります。

関係のある事前定義済みマクロは次のとおりです。
#ifdef __sun
Sun コンパイラ
#endif
#ifdef __SUNPRO_C
Sun C コンパイラ /* __SUNPRO_C の値はバージョン番号 */
#endif
#ifdef __SUNPRO_CC
Sun C++ コンパイラ /* __SUNPRO_CC の値はバージョン番号 */
#endif
#ifdef __sparc
SPARC アーキテクチャのコード生成
#endif
#ifdef __sparcv9
64 ビット SPARC アーキテクチャのコード生成
#endif
#ifdef __i386
Intel アーキテクチャのコード生成
#endif

ページ先頭へ


Q:
「標準」および「旧式」入出力ストリームの違いはなんですか。また、この問題に関する参考文献を教えてください。
A:
2 つのライブラリの設計と実装がまったく異なります。単純な入出力のプログラミングインタフェースは酷似しています。しかし、ユーザー独自のストリームクラスやマニピュレータの書き込みなど、複雑な動作については大きく異なります。
本バージョンの「旧式」入出力ストリームライブラリは、C++ 3.x および 4.x に付属するバージョンと互換性があります。このリリースに付属するマニュアルのほかにも、次のような参考文献があります。
  • Steve Teale 著
    C++ IOStreams Handbook
    Addison-Wesley 1993
標準入出力ストリームライブラリについては、C++ Standard で説明されています。その他にも、次のような参考文献があります。
  • Nicolai Josuttis 著
    The C++ Standard Library
    Addison-Wesley 1999
    (C++ 標準ライブラリ全般に関するチュートリアル)
  • Angelika Langer、 Klaus Kreft 共著
    Standard C++ IOStreams and Locales
    Addison-Wesley 1999
    (入出力ストリームとロケールに関するチュートリアル)
単純な入出力を実装するためのソースコードは、どちらの入出力ストリームも似通っています。簡単に移行するために、規格外のヘッダー <iostream.h>、<fstream.h>、および <strstream.h> が標準の入出力ストリームに装備されています。これらのヘッダーは、旧式の入出力ストリームにあるグローバル名前空間と酷似した宣言セットを提供します。
たとえば、次のコードは、Sun のコンパイラを使用して旧式と標準の両方の入出力ストリームで機能します。 ただし、一部のコンパイラは使用されません。

#include

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 による単純 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:
製品パッチの最新情報については、開発者向けポータルを確認してください。
製品パッチは、 SunSolve からダウンロードできます。

ページ先頭へ


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>
extern int x;
double z1 = sin(x); // 現在ではあいまい
double z2 = sin( (double)x ); // OK
float z3 = sin( (float)x ); // OK
long double z4 = sin( (long double)x ); // OK

下記の Solaris パッチは、Solaris 8 および 9 用の libm パッチで実現されている ANSI C++ に準拠した <cmath> および <math.h> ライブラリサポートを提供します。

  • Solaris 9 sparc (PatchId 111722-04)
  • Solaris 9 i386 (PatchId 111728-03)
  • Solaris 8 sparc (PatchId 111721-04)
  • Solaris 8 i386 (PatchId 112757-01)

ページ先頭へ


Q:
互換モード (-compat) のコードと標準モードのコードを混在させることはできますか ?
A:
Sun では混在を推奨しません。 「プラグイン」や動的読み込みライブラリであっても、次の理由から、同じプログラム内のコードの混在はサポートしていません。
  • クラスオブジェクトの配置が異なる。
  • 関数の呼び出し処理が異なる。
  • 「名前の符号化」処理が異なる。
  • 例外処理方法が矛盾する。
  • 2 つの入出力ストリームオブジェクトを同じファイル記述子に接続すると問題が発生する。
プログラムの 2 つの部分 (互換モードと標準モード) が通信しないときでも、コード内で例外が送出 (スロー) されると、プログラムがすぐにクラッシュする可能性があります。

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

ページ先頭へ


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 ファイルがある場合は、次のいずれかの操作を行います。

  • .h ファイルまたは .cc ファイルの名前を変更し、検索時に名前が一致するのを防ぎます。
  • -template=no%extdef オプションを指定して、テンプレート定義ファイルの自動検索を無効にします。ただし、このオプションを指定すると、別個のテンプレート定義の検索がすべて無効になります。C++ 標準ライブラリは、コンパイラが別個の定義を検索することを必要とします。定義分離型モデルが使用できないので、テンプレート定義をすべて明示的にコードに含める必要があります。

テンプレート定義モデルについては、「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.cc
mv 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 つの解決策があります。
  • -xcode=pic13 または -xcode=32 を指定してオブジェクトファイルを再コンパイルします。ほとんどの場合、この方法が推奨されます。
  • -xcode=abs64 を指定してオブジェクトファイルを再コンパイルします。この方法では、動的ライブラリを共有できなくなります。各プロセスは、メモリーの別々の領域にコピーされたライブラリに再書き込みする必要があります。この方法は、パフォーマンス制約が厳しく、システム共有が少ない環境で長時間、実行するアプリケーションに適しています。

ページ先頭へ


Q:
「SunWS_cache: エラー: SunWS_cache のロックの試みが失敗しました」というメッセージが表示される原因は何ですか ?
A:

テンプレートキャッシュの「lock attempt failed (ロックの試みが失敗しました)」というエラーメッセージには、主に次の 2 つの原因が考えられます。

  • キャッシュのロックが解放されずにコンパイルが中断または強制終了することがあります。この問題は、コンパイラの旧バージョンで発生します。コンパイラの新バージョンや、最新パッチを適用した旧バージョンでは、コンパイラの終了方法にかかわらずロックが解放されます。ロックファイルを削除することはできますが、キャッシュが壊れていて、さらに問題が発生する可能性があります。安全なのは、テンプレートキャッシュ全体を削除することです。
  • コンパイラプロセスからテンプレートキャッシュに書き込みが可能である必要があります。詳細は、umask(1) のマニュアルページを参照してください。特に、キャッシュまたはキャッシュ内のファイルを作成するプロセスの umask に、同じキャッシュにアクセスする必要があるほかのプロセスから書き込みできる必要があります。ディレクトリが NFS ファイルシステムにマウントされている場合は、システムは読み取りと書き込みが可能なようにマウントされている必要があります。

ページ先頭へ


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 関数の次の多重定義が定められています。
  • <stdlib.h> と <cstdlib>
    int abs(int);
    long abs(long);
  • <math.h> と <cmath>
    float abs(float);
    double abs(double);
    long double abs(long double);

最近まで、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); // エラー
double root_2 = sqrt(2.0); // OK
double x = sqrt(int_value); // エラー
double x = sqrt(double(int_value)); // OK

ページ先頭へ


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

解決策:

  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 宣言を使用するか、名前を明示的に修飾してください。

ページ先頭へ


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 つの代替手段を次に示します。

  • リンカー行に -instances=extern も必要です。
  • オブジェクトファイルを直接的に最終ディレクトリに生成します。テンプレートキャッシュも同じディレクトリに配置されます。
    次の例は使用しないでください。
    example% CC -c -instances=extern f1.cc
    example% mv f1.o /new/location/for/files
    代わりに次の例を使用してください。
    example% CC -c -instances=extern f1.cc -o /new/location/for/files/f1.o
    makefile マクロでプロセスをカプセル化できます。
  • 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

ページ先頭へ


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 ポインタのチェック機能がありません。これだけに限りませんが、次のようなことが挙げられます。

  • チェック機能を持たせると、誤った安心感を与える。プログラマに、printf() への NULL ポインタ引き渡しがうまくいったと思い込ませてしまいます。
  • プログラマが移植性のないコードを書くことを助長する。ANSI C、XPG3、XPG4、SVID2、および SVID3 では、 printf(%s, pointer) には NULL で終わる文字の配列への pointer ポイントがなければならないと規定されています。
  • デバッグが難しくなる。プログラマが NULL ポインタを printf() に渡してからプログラムがコアに入るのであれば、デバッガを使用すれば、不正なポインタを指定した printf() 呼び出しを簡単に見つけ出すことができます。しかし、 printf() が "(NULL ポインタ)" と出力することにより、そのバグを非表示にした場合、その後で進行するほかのプログラムは、ある実際のデータが欲しいときに、"(NULL ポインタ)" を解釈しようとするでしょう。その時点では、実際の問題がどこに隠れているか判断することは不可能かもしれません。
NULL ポインタを *printf に渡すアプリケーションがある場合は、ロケーション 0 に値 0 を設定するためのメカニズムを提供する特定の共有オブジェクト /usr/lib/0@0.so.1 を使用することができます。 このライブラリは、NULL ポインタの型による違いに関連するすべてのエラーをマスクするので、このライブラリは、コードを修正するまでの一時的な回避策としての使用に限定してください。

ページ先頭へ


Q:
sqrt() の呼び出し方法によって、複素数の平方根の虚部の符号が異なります。これはなぜですか ?
A:
この関数の実装は、「C99 csqrt Annex G 仕様」で規定されています。たとえば、次のコード例の出力を見てください。

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

  • 互換モードで 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;
    }

  • 標準モードで 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;
    }

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

ページ先頭へ


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 {
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
}
};
};

ページ先頭へ


Q:
実行時に「pure virtual function call (純粋仮想関数呼び出し)」メッセージが表示される原因は何ですか ?
A:
プログラム内に、あるエラーが発生すると必ず、「pure virtual function call (純粋仮想関数呼び出し)」メッセージが表示されます。このエラーは、次の 2 つのどちらかの場合に起こります。
  • このエラーは、抽象クラスのコンストラクタまたはデストラクタから、外部関数に「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 を呼び出そうとします。

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

    class Abstract {
    public:
    virtual void m() = 0; // あとで提供するボディ
    void g();
    };

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

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

ページ先頭へ


Q:
派生クラスの仮想関数は、識別形式の異なる基底クラスの仮想関数を隠す、というのはなぜですか。ほかのコンパイラの場合、このコードには何も問題がありません。
A:
C++ の規則では、多重定義は 1 スコープ内でのみ認められ、スコープを超えるものは認められません。基底クラスは、派生クラスのスコープを取り巻くスコープ内にあると考えられます。そのため、派生クラス内で宣言された名前は、基底クラス内のあらゆる関数を隠し、多重定義できません。この基本的な C++ 規則は、ARM よりも旧式です。

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

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

class Base {
public:
virtual int foo(int);
virtual double foo(double);
};
class Derived :public Base {
public:
using Base::foo; // 多重定義セットに基底クラス関数を追加する
virtual double foo(double); // override base-class version
};

ページ先頭へ

ライブラリの互換性


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 以降、使用可能ですが、標準ライブラリ内でオンにすることはできません。 これらは下方互換性を残しているからです。下記は、無効にされた機能の失われた機能リストです。

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

  • <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 クラス:

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



  • <list> 内の list クラス:

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

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

  • <memory> 内の auto_ptr クラス:

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

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

  • <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()

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

  • <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*> {}
    template typename iterator_traits::difference_type distance(InputIterator first, InputIterator last);

ページ先頭へ


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

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> を使用します。

ページ先頭へ


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 の場合、コンパイラはインライン関数を通常の呼び出し可能な関数 (ライン外) として生成します。

  • +d を指定してコンパイルした場合
  • -g を指定してコンパイルした場合
  • 仮想関数と同じように関数のアドレスが必要な場合
  • コンパイラがインラインで生成できない制御構造が関数にある場合
  • 関数が複雑すぎる場合

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 点が考えられます。

  • ファイル内の関数のサイズと最適化レベル
    最適化度の高い大きな関数は処理に時間がかかり、大量のメモリーを必要とすることがあります。コードで大きなマクロを頻繁に使用している場合は、関数が小さく見えても、マクロを展開すると非常に大きくなる可能性があります。
    最適化を行わずにコンパイルしてみてください (no -xO? または -O? オプション)。コンパイル時間が短縮した場合は、原因は、ファイル内に非常に大きな関数が 1 つ以上あり、その最適化に時間とメモリーを要していることです。
    また、コンパイルに使用するコンピュータに十分な物理メモリーがあることも確認してください。十分なメモリーがないと、最適化フェーズで時間がかかることがあります。
  • インライン関数
    C と C++ のインライン関数は、コンパイル時にマクロのように機能します。関数呼び出しがインラインで展開されると、大量のコード行に変わる可能性があります。すると、コンパイラは 2 つ以上の小さな関数ではなく、1 つの大きな関数を処理することになります。
    通常は、関数のインライン化を無効にした方がコンパイル時間は短縮します。ただし、コンパイルされたコードの実行は低速になります。
    詳細は、「C++ ユーザーズガイド」の -xinline の説明と「インライン関数の使用」を参照してください。
  • C++ のクラステンプレート
    C++ テンプレートが指定されている場合、コンパイラは呼び出されたテンプレートに基づいてコードを生成します。コンパイラは、ソースコード 1 行から、1 つ以上のテンプレート関数を生成する必要がある場合があります。テンプレート自体がコンパイル時間を長くするのではありませんが、コンパイラは、元のソースコードを見てわかるよりも多くのコードを処理する必要があります。
    たとえば、標準ライブラリに関数がなかった場合、次のコード行ではコンパイラが 241 個の関数を生成する必要があります。
    cout << "value = " << x << endl;

ページ先頭へ


Q:
バージョン 4.2 と比べて、バージョン 5.0 および 5.1 のコンパイラのコンパイル時間は大幅に長くなっています。将来、この問題は解決されるのでしょうか ?
A:
5.1 のパッチ 01 から現行バージョンまででは、コンパイル時間が大幅に改善されています。コンパイラのパフォーマンスに満足できない場合、次の推奨事項に留意してください。
  1. 極端な場合、インライン化は膨大な時間がかかる原因になります。-xO4 または -xO5 のいずれかのオプションを使用すると、コードジェネレータがいくつかの関数を自動的にインライン化します。-xO3 のような低い最適化レベルを使用する必要があるかもしれません。 オプティマイザが特定関数を自動的にインライン化しないようにするには、-xinline オプションを使用できます。

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

ページ先頭へ


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 までのコンパイラでは、インライン化アルゴリズムが改良され、より多くの構文を理解するようになっています。しかし、それでも、構文を理解できないケースが存在します。そうしたケースを次に示します。
  • 5.2 から 5.6 の C++ コンパイラは、ほとんど実行されない関数呼び出しを展開しません。この変更は、コンパイル速度、出力コードサイズ、および実行時速度の均衡をとるために役立ちます。
    たとえば、静的変数の初期化に使用される式は 1 回だけ実行されるので、これらの式内の関数呼び出しは展開されません。インライン関数 func は、静的変数の初期化式で呼び出されているときに展開されなくても、ほかの場所でインライン化されている可能性があります。同様に、例外ハンドラの関数呼び出しもほとんど実行されないので展開されない可能性があります。
  • 再帰関数は、最初の呼び出しレベルまでのみインライン化されます。コンパイラは、再帰関数の呼び出しをあいまいにインライン化できません。現在の実装は、インライン化されている任意の関数への初回呼び出しの時点で停止します。

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

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

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

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

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

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

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

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

ページ先頭へ


Sun だからできる、Java システム最適化ソリューション
Web でシステムの悩みをお聞かせください。
詳細