JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
Oracle Solaris Studio 12.3: C++ ユーザーズガイド     Oracle Solaris Studio 12.3 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I C++ コンパイラ

1.  C++ コンパイラの紹介

2.  C++ コンパイラの使用方法

3.  C++ コンパイラオプションの使い方

パート II C++ プログラムの作成

4.  言語拡張

5.  プログラムの編成

6.  テンプレートの作成と使用

6.1 関数テンプレート

6.1.1 関数テンプレートの宣言

6.1.2 関数テンプレートの定義

6.1.3 関数テンプレートの使用

6.2 クラステンプレート

6.2.1 クラステンプレートの宣言

6.2.2 クラステンプレートの定義

6.2.3 クラステンプレートメンバーの定義

6.2.3.1 関数メンバーの定義

6.2.3.2 静的データメンバーの定義

6.2.4 クラステンプレートの使用

6.3 テンプレートのインスタンス化

6.3.1 テンプレートの暗黙的インタンス化

6.3.2 テンプレートの明示的インスタンス化

6.3.2.1 テンプレート関数の明示的インスタンス化

6.3.2.2 テンプレートクラスの明示的インスタンス化

6.3.2.3 テンプレートクラス関数メンバーの明示的インスタンス化

6.3.2.4 テンプレートクラスの静的データメンバーの明示的インスタンス化

6.4 テンプレートの編成

6.5 デフォルトのテンプレートパラメータ

6.6 テンプレートの特殊化

6.6.1 テンプレートの特殊化宣言

6.6.2 テンプレートの特殊化定義

6.6.3 テンプレートの特殊化の使用とインスタンス化

6.6.4 部分特殊化

6.7 テンプレートの問題

6.7.1 非局所型名前の解決とインスタンス化

6.7.2 テンプレート引数としての局所型

6.7.3 テンプレート関数のフレンド宣言

6.7.4 テンプレート定義内での修飾名の使用

6.7.5 テンプレート名の入れ子

6.7.6 静的変数や静的関数の参照

6.7.7 テンプレートを使用して複数のプログラムを同一ディレクトリに構築する

7.  テンプレートのコンパイル

8.  例外処理

9.  プログラムパフォーマンスの改善

10.  マルチスレッドプログラムの構築

パート III ライブラリ

11.  ライブラリの使用

12.  C++ 標準ライブラリの使用

13.  従来の iostream ライブラリの使用

14.  ライブラリの構築

パート IV 付録

A.  C++ コンパイラオプション

B.  プラグマ

用語集

索引

6.7 テンプレートの問題

この節では、テンプレートを使用する場合の問題について説明しています。

6.7.1 非局所型名前の解決とインスタンス化

テンプレート定義で使用される名前の中には、テンプレート引数によって、またはそのテンプレート内で、定義されていないものがある可能性があります。そのような場合にはコンパイラが、定義の時点で、またはインスタンス化の時点で、テンプレートを取り囲むスコープから名前を解決します。1 つの名前が複数の場所で異なる意味を持つために解決の形式が異なることも考えられます。

名前の解決は複雑です。したがって、汎用性の高い大域環境で提供されているもの以外は、非局所型名前に依存するべきではありません。言い換えれば、どこでも同じように宣言され、定義されている非局所型名前だけを使用するようにしてください。この例では、テンプレート関数の converter が、非局所型名前である intermediarytemporary を使用しています。これらの名前は use1.ccuse2.cc では異なる定義を持っているため、コンパイラが異なれば結果は違うものになるでしょう。テンプレートが正しく機能するためには、すべての非局所型名前 (intermediarytemporary) がどこでも同じ定義を持つ必要があります。

use_common.h
// Common template definition
template <class Source, class Target>
Target converter(Source source)
       {temporary = (intermediary)source;
       return (Target)temporary;}
use1.cc
typedef int intermediary;
int temporary;

#include "use_common.h"
use2.cc
typedef double intermediary;
unsigned int temporary;

#include "use_common.h"

非局所型名前を使用する典型的な例として、1 つのテンプレート内で cincout のストリームの使用があります。ほとんどのプログラマは実際、ストリームをテンプレートパラメータとして渡すことは望まないので、1 つの大域変数を参照するようにします。しかし、cincout はどこでも同じ定義を持っている必要があります。

6.7.2 テンプレート引数としての局所型

テンプレートインスタンス化の際には、型と名前が一致することを目安に、どのテンプレートがインスタンス化または再インスタンス化される必要があるか決定されます。したがって、局所型がテンプレート引数として使用された場合には重大な問題が発生する可能性があります。自分のコードに同様の問題が生じないように注意してください。

例 6-1 テンプレート引数としての局所型の問題の例

array.h
template <class Type> class Array {
        Type* data;
        int   size;
    public:
        Array(int sz);
        int GetSize();
};

array.cc
template <class Type> Array<Type>::Array(int sz)
    {size = sz; data = new Type[size];}
template <class Type> int Array<Type>::GetSize()
    {return size;}

file1.cc
#include "array.h"
struct Foo {int data;};
Array<Foo> File1Data(10);

file2.cc
#include "array.h"
struct Foo {double data;};
Array<Foo> File2Data(20);

file1.cc に登録された Foo 型は、file2.cc に登録された Foo 型と同じではありません。局所型をこのように使用すると、エラーと予期しない結果が発生することがあります。

6.7.3 テンプレート関数のフレンド宣言

テンプレートは、使用前に宣言されている必要があります。フレンド宣言では、テンプレートを宣言するのではなく、テンプレートの使用を宣言します。フレンド宣言の前に、実際のテンプレートが宣言されている必要があります。次の例では、作成済みオブジェクトファイルをリンクしようとするときに、operator<< 関数が未定義であるというエラーが生成されます。その結果、operator<< 関数はインスタンス化されません。

例 6-2 フレンド宣言の問題の例

array.h
// generates undefined error for the operator<< function
#ifndef ARRAY_H
#define ARRAY_H
#include <iosfwd>

template<class T> class array {
    int size;
public:
    array();
    friend std::ostream&
        operator<<(std::ostream&, const array<T>&);
};
#endif

array.cc
#include <stdlib.h>
#include <iostream>

template<class T> array<T>::array() {size = 1024;}

template<class T>
std::ostream&
operator<<(std::ostream& out, const array<T>& rhs)
    {return out <<’[’ << rhs.size <<’]’;}

main.cc
#include <iostream>
#include "array.h"

int main()
{
    std::cout
      << "creating an array of int... " << std::flush;
    array<int> foo;
    std::cout << "done\n";
    std::cout << foo << std::endl;
    return 0;
}

コンパイラは、次の行を array クラスの friend である正規関数の宣言として読み取っているので、コンパイル中にエラーメッセージは発行されません。

friend ostream& operator<<(ostream&, const array<T>&);

operator<< は実際にはテンプレート関数であるため、template class array を宣言する前にこの関数にテンプレート宣言を行う必要があります。ただし、operator<< はパラメータ type array<T> を持つため、関数宣言の前に array<T> を宣言する必要があります。ファイル array.h は、次の例のようになります。

#ifndef ARRAY_H
#define ARRAY_H
#include <iosfwd>

// the next two lines declare operator<< as a template function
template<class T> class array;
template<class T>
    std::ostream& operator<<(std::ostream&, const array<T>&);

template<class T> class array {
    int size;
public:
    array();
    friend std::ostream&
      operator<< <T> (std::ostream&, const array<T>&);
};
#endif

6.7.4 テンプレート定義内での修飾名の使用

C++ 標準は、テンプレート引数に依存する修飾名を持つ型を、typename キーワードを使用して型名として明示的に示すことを規定しています。この要件は、コンパイラがそれを型として推定できる場合にも適用されます。次の例の各コメントは、それぞれの修飾名が typename キーワードを必要とするかどうかを示しています。

struct simple {
  typedef int a_type;
  static int a_datum;
};
int simple::a_datum = 0; // not a type
template <class T> struct parametric {
  typedef T a_type;
  static T a_datum;
};
template <class T> T parametric<T>::a_datum = 0;   // not a type
template <class T> struct example {
  static typename T::a_type variable1;             // dependent
  static typename parametric<T>::a_type variable2; // dependent
  static simple::a_type variable3;                 // not dependent
};
template <class T> typename T::a_type             // dependent
  example<T>::variable1 = 0;                      // not a type
template <class T> typename parametric<T>::a_type // dependent
  example<T>::variable2 = 0;                      // not a type
template <class T> simple::a_type   // not dependent
example<T>::variable3 = 0;          // not a type

6.7.5 テンプレート名の入れ子

>>」という文字シーケンスは右シフト演算子と解釈されるため、あるテンプレート名を別のテンプレート名で使用する場合は注意が必要です。隣接する「>」文字を少なくとも 1 つの空白文字で区切ってください。

次に誤った書式の例を示します。

Array<String<10>> short_string_array(100); // >> = right-shift

これは次のように解釈されます。

Array<String<10 >> short_string_array(100);

正しい構文は次のとおりです。

Array<String<10> > short_string_array(100);

6.7.6 静的変数や静的関数の参照

テンプレート定義の内部では、大域スコープや名前空間で静的として宣言されたオブジェクトや関数の参照がサポートされません。複数のインスタンスが生成されると、それぞれのインスタンスが別々のオブジェクトを参照するため、単一定義規則 (C++ 標準の第 3.2 節) に違反します。通常、このエラーはリンク時にシンボルの不足の形で通知されます。

すべてのテンプレートのインスタンス化で同じオブジェクトを共有する場合は、そのオブジェクトを該当する名前空間の非静的メンバーにします。また、あるテンプレートクラスをインスタンス化するたびに、別々のオブジェクトを使用する場合は、そのオブジェクトを該当するテンプレートクラスの静的メンバーにします。同様に、あるテンプレート関数をインスタンス化するたびに、別々のオブジェクトを使用する場合は、そのオブジェクトを該当するテンプレート関数の局所メンバーにします。

6.7.7 テンプレートを使用して複数のプログラムを同一ディレクトリに構築する

-instances=extern を指定して複数のプログラムまたはライブラリを構築する場合は、それらを別のディレクトリに構築します。同一ディレクトリ内に構築する場合は、構築ごとにリポジトリを消去します。これにより、予期しないエラーが回避されます。詳細は、「7.4.4 テンプレートリポジトリの共有」を参照してください。

メイクファイル a.ccb.ccx.h、および x.cc. を使用した次の例を考えてみますこの例は、-instances=extern を指定した場合にのみ有効です。

........
Makefile
........
CCC = CC

all: a b

a:
    $(CCC) -I. -instances=extern -c a.cc
    $(CCC) -instances=extern -o a a.o

b:
    $(CCC) -I. -instances=extern -c b.cc
    $(CCC) -instances=extern -o b b.o

clean:
    /bin/rm -rf SunWS_cache *.o a b
...
x.h
...
template <class T> class X {
public:
  int open();
  int create();
  static int variable;
};
...
x.cc
...
template <class T> int X<T>::create() {
  return variable;
}

template <class T> int X<T>::open() {
  return variable;
}

template <class T> int X<T>::variable = 1;
...
a.cc
...
#include "x.h"

int main()
{
  X<int> temp1;

  temp1.open();
  temp1.create();
}
...
b.cc
...
#include "x.h"

int main()
{
  X<int> temp1;

  temp1.create();
}

ab の両方を構築する場合は、それらの 2 つの構築の間に make clean コマンドを追加します。次のコマンドでは、エラーが発生します。

example% make a
example% make b

次のコマンドでは、エラーは発生しません。

example% make a
example% make clean
example% make b