9

これに似たビジタークラスがあります。

struct Visitor 
{
    template <typename T>
    void operator()(T t)
    {
        ...
    }

    void operator()(bool b)
    {
        ...
    }
};

明らかにoperator()(bool b)、前述のテンプレート関数の特殊化を目的としています。

ただし、そのtemplate<>前に見慣れた構文がなく、これをテンプレートの特殊化として宣言しています。しかし、それはコンパイルします。

これは安全ですか?これは正しいです?

4

5 に答える 5

20

あなたのコードはテンプレートの特殊化ではなく、テンプレート化されていない関数です。そこにはいくつかの違いがあります。テンプレート化されていない operator() は、テンプレート化されたバージョンよりも優先されます (完全一致の場合、型変換は行われません) が、テンプレート化された関数を強制的に呼び出すことができます。

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
}
于 2009-06-01T22:48:07.450 に答える
5

ここにあるのは関数のオーバーロードです。template <>テンプレートの特殊化を取得するには、実際に構文が必要です。ただし、これらの 2 つのアプローチは、同じように見えても微妙に異なり、呼び出す関数を適切に選択する際にコンパイラでさえ迷ってしまう可能性があることに注意してください。考えられるすべてのケースをリストすると、この回答には少し長すぎますが、件名についてHerb Sutter GoTW #49を確認することをお勧めします。

于 2009-06-01T22:41:53.687 に答える
4

ああ、コンパイルします。テンプレート関数にはなりません。テンプレートの特殊化の代わりに、通常の非テンプレート関数があります。

それは安全であり、実際にはあなたが望むものでもあるでしょう。通常、Visitorパターンはオーバーロードによって実装されます。とにかく、関数テンプレートを特殊化することは実際には良い考えではありません。

于 2009-06-01T22:27:07.680 に答える
3

テンプレートのシリアル化ではなく、関数のオーバーロードを実行しました。安全。

PS何を達成しようとしているのかを知らずに、それが正しいかどうかを判断するのは難しいです。テンプレートであろうとオーバーロードされた関数であろうと、コンパイル時に演算子が選択されることに注意してください。実行時のディスパッチが必要な場合は、オーバーロードではなく、ポリモーフィズムが必要です。まあ、とにかくそれをおそらく知っているでしょう。念のため。

于 2009-06-01T22:32:17.543 に答える
2

あなたが持っている

  • void operator()(bool b)それは非テンプレート関数です
  • template< typename T > void operator()(T t)これは、上記をオーバーロードする別の基本テンプレートです

一致しなかっtemplate<> void operator(int i)た場合にのみ考慮されるように、2 番目のものを完全に特殊化することができます。void operator()(bool b)

ベース テンプレートの特殊化は、呼び出すベース テンプレート メソッドを選択するために使用されます。ただし、あなたの場合、最初に考慮されるテンプレート化されていないメソッドがあります。

記事Why Not Specialize Function Templates? メソッドがどのように選択されるかについての非常に良い説明を提供します。

要約すると:

  1. 非テンプレート関数が最初に考慮されます (これは上記の単純な operator()(bool) です)
  2. 関数ベース テンプレートは 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
于 2009-06-01T23:11:34.250 に答える