テンプレートの専門化にはテンプレート<>構文が必要ですか?
-
06-09-2019 - |
質問
私はこれに似た訪問者クラスを持っています:
struct Visitor
{
template <typename T>
void operator()(T t)
{
...
}
void operator()(bool b)
{
...
}
};
明らかに、 operator()(bool b)
前述のテンプレート関数の専門化を目的としています。
しかし、それはありません template<>
私がそれの前に見ることに慣れている構文で、これをテンプレートの専門化として宣言しています。しかし、コンパイルします。
これは安全ですか?これは正しいです?
解決
あなたのコードはテンプレートの専門化ではなく、テンプレートのない関数です。そこにはいくつかの違いがあります。テンプレートされていないオペレーター()は、テンプレートバージョンよりも優先されます(正確な一致の場合、タイプ変換はそこでは行われません)が、テンプレートの関数を強制的に呼び出すことができます。
class Visitor
{
public: // corrected as pointed by stefanB, thanks
template <typename T>
void operator()( T data ) {
std::cout << "generic template" << std::endl;
}
void operator()( bool data ) {
std::cout << "regular member function" << std::endl;
}
};
template <> // Corrected: specialization is a new definition, not a declaration, thanks again stefanB
void Visitor::operator()( int data ) {
std::cout << "specialization" << std::endl;
}
int main()
{
Visitor v;
v( 5 ); // specialization
v( true ); // regular member function
v.operator()<bool>( true ); // generic template even if there is a non-templated overload
// operator() must be specified there (signature of the method) for the compiler to
// detect what part is a template. You cannot use <> right after a variable name
}
コードでは大きな違いはありませんが、コードがテンプレートパラメータータイプを渡す必要がある場合は、面白くなります。
template <typename T>
T g() {
return T();
}
template <>
int g() {
return 0;
}
int g() {
return 1;
}
int main()
{
g<double>(); // return 0.0
g<int>(); // return 0
g(); // return 1 -- non-templated functions take precedence over templated ones
}
他のヒント
ここにあるのは、関数の過負荷です。テンプレートの専門化を取得するには、実際に必要です template <>
構文。ただし、これらの2つのアプローチは、たとえそれらが同一に見えても、微妙に異なっていても、コンパイラでさえも正しい関数を選択するときに迷子になる可能性があることに注意する必要があります。考えられるすべてのケースをリストすることは、この答えには少し長すぎますが、確認することをお勧めします ハーブサッターGOTW#49 件名に。
ああ、コンパイルします。テンプレート関数ではありません。テンプレートの専門化の代わりに、通常の非テンプレート関数があります。
それは安全であり、実際にあなたが望むものもおそらくそうです。訪問者パターンは通常、オーバーロードによって実装されます。専門機能テンプレート とにかく本当に良い考えではありません。
あなたがしたことは、テンプレートのシリアル化ではなく、関数の過負荷です。安全。
PSあなたが達成しようとしていることを知らずに、それが正しいかどうかを言うのは難しいです。テンプレートまたは過負荷機能であっても、オペレーターはコンパイル時間に選択されることに注意してください。実行時の派遣が必要な場合は、過負荷ではなく、多型が必要です。とにかく、あなたはおそらくそれを知っているでしょう。念のため。
あなたが持っている
void operator()(bool b)
それは非テンプレート関数ですtemplate< typename T > void operator()(T t)
これは、上記を過負荷にする別のベーステンプレートです
2番目のものを完全に専門化することができます template<> void operator(int i)
これはいつだけ考慮されます void operator()(bool b)
一致しませんでした。
ベーステンプレートの専門化は、呼び出すベーステンプレートメソッドを選択するために使用されます。ただし、あなたの場合、最初に考慮される非テンプレート方法があります。
記事 機能テンプレートを専門化してみませんか? 方法がどのように選択されているかについて非常に良い説明をします。
Sumary:
- テンプレート以外の関数が最初に見なされます(これはあなたのプレーンオペレーター()(上記のbool)です)
- 関数ベーステンプレートは2番目にチェックされます(これはテンプレート機能です)、最も専門化されたベーステンプレートが選択され、その後、特殊化が使用される正確なタイプの専門化がある場合は、ベーステンプレートが「正しい」タイプで使用されます(参照記事の説明)
例:
#include <iostream>
using namespace std;
struct doh
{
void operator()(bool b)
{
cout << "operator()(bool b)" << endl;
}
template< typename T > void operator()(T t)
{
cout << "template <typename T> void operator()(T t)" << endl;
}
};
// note can't specialize inline, have to declare outside of the class body
template<> void doh::operator()<>(int i)
{
cout << "template <> void operator()<>(int i)" << endl;
}
template<> void doh::operator()<>(bool b)
{
cout << "template <> void operator()<>(bool b)" << endl;
}
int main()
{
doh d;
int i;
bool b;
d(b);
d(i);
}
あなたは電話を受けます:
operator()(bool b) <-- first non template method that matches
template <> void operator()(int i) <-- the most specialized specialization of templated function is called