7
#include <iostream>
template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

#1と#2の違いは何ですか。TEST を定義すると、#1 が機能します。しかし、私がコメントすると、#3の作業...そして、関数テンプレートの特殊化を書く正しい方法は...

4

3 に答える 3

3

#1 は #3 の関数テンプレートの特殊化を宣言し、テンプレート パラメーターを自動的に推定します。#2 はT=int*. #3の特殊化はできません。T指定されたものに置き換えるとパラメーターint*につながるためです。int**

を呼び出すとfoo、オーバーロードの解決により、最初に最適なベース テンプレートが選択され、次にそのテンプレートに既存の特殊化がないかどうかがチェックされます。定義済みの場合、TEST2 つの基本テンプレート (#0 と #3) があり、#3 の方がより一致して選択されます。次に、コンパイラはそのテンプレートの特殊化をチェックし、#1 がより適切であり、呼び出されています。

定義されていないTEST場合でも、2 つの基本テンプレート (#0 と #3) があり、#3 の方がより一致して選択されます。次に、コンパイラはそのテンプレートの特殊化をチェックしますが、#2 は #3 ではなく #0 を特殊化するため、考慮されず、#3 の呼び出しが終了します。

これはWhy not Specialize Function Templatesの古典的な例です。そこでは、問題がより詳細に説明されています。

簡単な解決策は、関数テンプレートをまったく特殊化せず、特殊な型に新しいオーバーロードを追加するだけです。

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}
于 2012-01-21T12:19:03.380 に答える
2

関数テンプレートの特殊化では、テンプレート引数を明示的にリストできますが、テンプレート引数が推定される場合はその必要はありません。テンプレート引数を指定しない場合、オーバーロードの解決と同じ規則を使用して、コンパイラによって推定されます。どの関数のオーバーロードを選択するかを決定するために、コンパイラはプライマリ テンプレート (最初に魔法のプロセスによって選択される) のみを調べることから始めます。利用可能な 2 つのプライマリ テンプレートを見る

template <typename T> void foo(T);
template <typename T> void foo(T*);

後者は、ポインター引数に適しています。適切なプライマリ テンプレートが見つかると、コンパイラは、このプライマリ テンプレートの潜在的な特殊化を探します。ただし、例#2は、ポインター引数が含まれていますが、実際にはポインター引数を取る関数テンプレートの特殊化ではありません。一次申告を取る場合

template <typename T> void foo(T*);

Tそして、取得した明示的に指定されたテンプレート引数に置き換えint*ます

template <> void foo<int*>(int**);

つまり、宣言

template <> void foo<int*>(int*);

は何か違う。テンプレート引数を指定するときにポインターを失いたいだけかもしれません。

template <> void foo<int>(int*);
于 2012-01-21T12:10:19.537 に答える
0

#2 がどの関数を特殊化する必要があるか、または非常に複雑なオーバーロード解決ルールが呼び出す関数をどのように選択するかを正確に知ることはできません。

ほとんどの場合、関数を特殊化する必要はありませんが、代わりにオーバーロードに依存できることは知っています。int*必要な機能を取得するには

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

パラメータが一致する限り、非テンプレート関数はテンプレートよりも優先されます。

于 2012-01-21T12:09:00.497 に答える