一、関数テンプレート(Function Template)

23664 ワード

***
1:関数テンプレート(Function Template)
関数テンプレートとは、パラメータによって表される一連の関数です.関数テンプレートは、異なるタイプのパラメータによって呼び出され、通常の関数機能と同様に使用されます.異なるのは、関数テンプレートが定義されているときのパラメータのタイプが未知であることです.
1.1.、例、テンプレート定義
template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    // if a < b then use b else use a 
    return a

以上のコードの機能は2つの値の中の最大値を求めることであり,パラメータのタイプは不確定であり,ここでテンプレートパラメータTと宣言する.テンプレートパラメータの宣言は次のとおりです.
template < comma-separated-list-of-parameters > 

上記の例のパラメータは、一対の<>によって拡張される.typenameはキーワードであり、Tはタイプパラメータ識別であり、複数のパラメータ識別子がある場合は」と区切ります.もちろんパラメータ識別子は他の識別子を用いてもよいが,T(TはTypeの頭文字)を慣用しているだけである.
注意:キーワードtypenameはclassで代用される場合もありますが、本シリーズの学習ノートではtypename、キーワードclassはクラスのみを表すために使用されます.
1.2、テンプレートの使用
#include  
#include  
#include "max.hpp" 

int main() 
{ 
    int i = 42; 
    std::cout << "max(7,i): " << ::max(7,i) << std::endl; 

    double f1 = 3.4; 
    double f2 = -6.7; 
    std::cout << "max(f1,f2): " << ::max(f1,f2) << std::endl; 

    std::string s1 = "mathematics"; 
    std::string s2 = "math"; 
    std::cout << "max(s1,s2): " << ::max(s1,s2) << std::endl; 
} 

結果は次のとおりです.
max(7,i):   42 
max(f1,f2): 3.4 
max(s1,s2): mathematics

上記の例では関数テンプレートmax()が3回呼び出され、呼び出されるたびに使用されるパラメータタイプはint,double,stringである
注意:max()を呼び出す前に、標準テンプレートライブラリにもstd::max()のテンプレート関数があり、::max()が上記で定義したmax()関数を使用していることを確認できます.
上記の手順は,3つの異なるタイプのパラメータを1つのmax()関数で処理するように見えるが,実際にはそうではない.我々の上の関数テンプレートmax()は,被コンパイラが呼び出し時の異なるタイプに基づいて3つの関数体を生成する.
intを使用する関数体:
inline int const& max (int const& a, int const& b) 
{ 
    // if a < b then use b else use a 
    return a

他の2種類の関数:
const double& max (double const&, double const&); 
const std::string& max (std::string const&, std::string const&); 

テンプレートパラメータを特定のタイプで置換するエンジニアリングをインスタンス化と呼ぶ.インスタンス化の結果、実行が呼び出しである真の関数インスタンスが生成されます.
注意:関数テンプレートが呼び出されるとインスタンス化されます.
テンプレート関数を1つのタイプでインスタンス化しようとすると、そのタイプがテンプレート内の操作をサポートしていない場合、コンパイルエラーが発生します.たとえば、次のようにします.
std::complex c1, c2;    // doesn't provide operator < 
… 
max(c1,c2);                    // ERROR at compile time 

上記の例では、比較演算子"
プログラムをコンパイルすると、実際のプロセステンプレートが2回コンパイルされます.-まず、セミコロンが欠けているなどのテンプレートコードの構文エラーをチェックします.-次に、インスタンス化時にテンプレート関数を呼び出すときに、テンプレート関数の操作がこのタイプをサポートしているかどうかをチェックします.
2、パラメータ導出(Argument Deduction)
上記の例では、max()が呼び出されると、テンプレートパラメータは、呼び出されたときのパラメータに基づいて導出される.上にintタイプが入ってくると,C++コンパイラはTがintでなければならないと推測する.自動導出にはタイプ変換がないため、入力されたタイプは厳密に一致する必要があります.
template  
inline T const& max (T const& a, T const& b); 
… 
max(4,7)     // OK: T is int for both arguments 
max(4,4.2)   // ERROR: first T is int, second T is double

2番目のmax()の呼び出しは、1番目のパラメータがintであるため、2番目のdoubleである.したがって、コンパイル時にエラーが発生します.このエラーには、3つの方法があります.
1、パラメータを変換し、二つのパラメータを厳密に一致させる
max(static_cast<double>(4),4.2)    // OK 

2、指定タイプTを表示
max<double>(4,4.2)                 // OK 

3、関数テンプレートの定義時に2つの異なるタイプのパラメータを指定する
template <typename T1,typename T2> 
inline T const& max (T2 const& a, T2 const& b) 

3、テンプレートパラメータ(Template Parameters)
関数テンプレートには、次の2種類のパラメータがあります.
1、関数テンプレート名の前に、テンプレートパラメータ、カッコ"<>"を使用して宣言します.
template <typename T>            // T is template parameter 

2、パラメータを呼び出し、括弧を使用する()」()を使用して、関数テンプレート名の後に宣言する
max (T const& a, T const& b)   // a and b are call parameters 

テンプレートパラメータの個数は任意ですが、テンプレートパラメータにデフォルト値を指定することはできません.
たとえば、関数テンプレートmax()の2つの異なるタイプを指定できます.
template <typename T1, typename T2> 
inline T1 max (T1 const& a, T2 const& b) 
{ 
    return a < b ? b : a; 
} 
… 
max(4,4.2)   // OK, but type of first argument defines return type 

上の例はよさそうですが、問題があります.まず、戻りタイプを宣言する必要があります.戻りタイプがテンプレートパラメータタイプの1つである場合、別のパラメータタイプが戻りタイプに変換される可能性があります.もう1つの問題は、2番目のタイプを1番目のタイプに変換するとローカル一時オブジェクトが生成され、参照方式(by reference)で結果が返されないため、上記の例では、T const&ではなくT 1が返されることになります.
呼び出しパラメータ(call parameters)はテンプレートパラメータ(template parameters)で構成されているため、両者は関連している.この概念を関数テンプレートパラメータ導出と呼ぶ.導出があれば、通常の関数を呼び出すように関数テンプレートを呼び出すことができる.前述の例のように、呼び出し時に関数テンプレートに指定されたタイプを表示する
template <typename T> 
inline T const& max (T const& a, T const& b); 
… 
max<double>(4,4.2)    // instantiate T as double 

テンプレートパラメータと呼び出しパラメータに直接関係がなく、コンパイラもテンプレートパラメータを導出できない場合は、テンプレートパラメータを明確に指定する必要があります.たとえば、max()に戻りパラメータタイプとして3番目のテンプレートパラメータタイプを指定できます.
template <typename T1, typename T2, typename RT> 
inline RT max (T1 const& a, T2 const& b);

しかし、導出機構は戻りタイプを一致させることはなく、テンプレートパラメータRTも呼び出しパラメータに含まれない.したがって、コンパイラはRTを導出することができず、呼び出し時に指定したタイプを表示する必要があります.以下のようにします.
template <typename T1, typename T2, typename RT> 
inline RT max (T1 const& a, T2 const& b); 
… 
max<int,double,double>(4,4.2)    // OK, but tedious 

上記の例では、呼び出し時にパラメータを完全にコンパイラによって導く必要がないか、すべてのパラメータを多く表示して指定します.もちろん、最初のテンプレートパラメータのみを明確に指定する方法もあります.他のパラメータはコンパイラによって自動的に導出されます.RTは最初のパラメータの位置に置きます.
template <typename RT, typename T1, typename T2> 
inline RT max (T1 const& a, T2 const& b); 
… 
max<double>(4,4.2)    // OK: return type is double 

4、関数テンプレートの再ロード(Overloading Function Templates)
通常の関数と同様に、関数テンプレートも再ロードできます.関数のリロード:異なる関数の定義には同じ関数名があり、関数が呼び出されたときにコンパイラがどの関数を使用するかを判断します.次のように、関数テンプレートの再ロードの例を示します.
// maximum of two int values 
inline int const& max (int const& a, int const& b) 
{     
    return a// maximum of two values of any type 
template  
inline T const& max (T const& a, T const& b) 
{
    return a// maximum of three values of any type 
template  
inline T const& max (T const& a, T const& b, T const& c) 
{ 
    return max (max(a,b), c); 
}

int main() 
{ 
    ::max(7, 42, 68);     // calls the template for three arguments 
    ::max(7.0, 42.0);     // calls max (by argument deduction) 
    ::max('a', 'b');      // calls max (by argument deduction) 
    ::max(7, 42);         // calls the nontemplate for two ints 
    ::max<>(7, 42);       // calls max (by argument deduction) 
    ::max<double>(7, 42); // calls max (no argument deduction) 
    ::max('a', 42.7);     // calls the nontemplate for two ints 
} 

上記の例では、非テンプレート関数は、同名の関数テンプレートと同時に存在してもよいし、同じタイプの関数テンプレートインスタンスと同時に存在してもよい.すべての条件が同じである場合、コンパイラはテンプレート以外の関数を優先的に選択します.したがって、上記の4番目の呼び出しは非テンプレート関数です.
max(7, 42)      // both int values match the nontemplate function perfectly 

ただし、テンプレートがより一致する場合は、次のようなテンプレートのインスタンス化関数を使用します.
max(7.0, 42.0)  // calls the max (by argument deduction) 
max('a', 'b');  // calls the max (by argument deduction) 

呼び出し時に空のテンプレートパラメータリスト"<>"を使用すると、コンパイラが関数テンプレートからのインスタンスを使用する必要があり、テンプレートパラメータが呼び出しパラメータによって自動的に導出されることを示します.
max<>(7, 42)    // calls max (by argument deduction) 

テンプレートは自動タイプ変換できないため、通常の関数は自動タイプ変換できます.最後に呼び出されたのは非テンプレート関数です.
max('a', 42.7)      // only the nontemplate function allows different argument types 

より有用な例は、ポインタタイプとC-styleの文字列にmax()テンプレートを再ロードすることです.
#include  
#include  
#include  

// maximum of two values of any type 
template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    return a < b ? b : a; 
} 

// maximum of two pointers 
template <typename T> 
inline T* const& max (T* const& a, T* const& b) 
{ 
    return *a < *b ? b : a; 
} 

// maximum of two C-strings 
inline char const* const& max (char const* const& a, 
                               char const* const& b) 
{ 
    return std::strcmp(a,b) < 0 ? b : a; 
} 

int main () 
{ 
    int a=7; 
    int b=42; 
    ::max(a,b);      // max() for two values of type int 

    std::string s="hey"; 
    std::string t="you"; 
    ::max(s,t);      // max() for two values of type std::string 

    int* p1 = &b; 
    int* p2 = &a; 
    ::max(p1,p2);    // max() for two pointers 

    char const* s1 = "David"; 
    char const* s2 = "Nico"; 
    ::max(s1,s2);    // max() for two C-strings 
} 

上記のすべてのリロード関数の多くは、リファレンス(by reference)です.一般的に、リロード関数の間にはこのような明らかな違いがあるか、パラメータの個数が異なるか、パラメータのタイプが明らかに異なるか、違いが明らかでないと思わぬ問題が発生する可能性があります.以下のように、リファレンス(by reference)のmax()を値(by value)でリロードします.3つのパラメータのmax()関数を使用して、C-style文字列の最大値を取得できません.
// maximum of two values of any type (call-by-reference) 
template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    return a < b ? b : a; 
} 

// maximum of two C-strings (call-by-value) 
inline char const* max (char const* a, char const* b) 
{ 
    return std::strcmp(a,b) < 0 ? b : a; 
} 

// maximum of three values of any type (call-by-reference) 
template <typename T> 
inline T const& max (T const& a, T const& b, T const& c) 
{ 
    return max (max(a,b), c); // error, if max(a,b) uses call-by-value 
} 

int main () 
{ 
    ::max(7, 42, 68); // OK 

    const char* s1 = "frederic"; 
    const char* s2 = "anica"; 
    const char* s3 = "lucas"; 
    ::max(s1, s2, s3); // ERROR 

} 

上記の例では、C-style文字列のmax(a,b)リロード関数が新しいローカル値を作成し、変更値が参照で返されるため、エラーが発生します.
上記の例は、微細な重荷による予期せぬ挙動である.関数が呼び出されると、すべてのリロード関数が現在の範囲で表示されない場合は、上記の問題が発生するか、発生しない可能性があります.以下のように、
// maximum of two values of any type 
template <typename T> 
inline T const& max (T const& a, T const& b) 
{ 
    return a// maximum of three values of any type 
template <typename T> 
inline T const& max (T const& a, T const& b, T const& c) 
{ 
    return max (max(a,b), c);  // uses the template version even for ints 
}                              // because the following declaration comes 
                               // too late: 
// maximum of two int values 
inline int const& max (int const& a, int const& b) 
{ 
    return a

intタイプのmax()非テンプレート関数は呼び出し後に定義されるため、「return max(max(a,b),c)、関数テンプレートのintインスタンスが呼び出される.
5、まとめ
1、関数テンプレートは異なるテンプレートパラメータに対して一連の関数を定義することができる.2、関数テンプレートは呼び出しパラメータのタイプによって、異なるタイプの関数をインスタンス化する.3、呼び出し時に指定した目標パラメータを表示できます.4、関数テンプレートも再ロードできます.5、関数テンプレートのリロードは、異なるリロード関数の間に明らかな違いがあることが望ましい.6、すべての関数が呼び出される前に定義されることを保証しなければならない.