25

テンプレート クラスの特定のメンバーを特殊化することは可能ですか? 何かのようなもの:

template <typename T,bool B>
struct X
{
    void Specialized();
};

template <typename T>
void X<T,true>::Specialized()
{
    ...
}

template <typename T>
void X<T,false>::Specialized()
{
    ...
}

もちろん、このコードは無効です。

4

2 に答える 2

30

すべてのテンプレート引数を指定することによってのみ、明示的に特殊化できます。クラス テンプレートのメンバー関数の部分的な特殊化は許可されていません。

template <typename T,bool B>
struct X
{
    void Specialized();
};

// works
template <>
void X<int,true>::Specialized()
{
    ...
}

回避策は、オーバーロードされた関数を導入することです。これには、同じクラスにあるという利点があるため、メンバー変数、関数、およびスタッフへの同じアクセスが可能です。

// "maps" a bool value to a struct type
template<bool B> struct i2t { };

template <typename T,bool B>
struct X
{
    void Specialized() { SpecializedImpl(i2t<B>()); }

private:
    void SpecializedImpl(i2t<true>) { 
      // ...
    }

    void SpecializedImpl(i2t<false>) { 
      // ...
    }
};

オーバーロードされた関数に渡し、テンプレート パラメーターを関数パラメーターにプッシュすることで、関数を任意に「特殊化」し、必要に応じてテンプレート化することもできます。もう 1 つの一般的な手法は、個別に定義されたクラス テンプレートに従うことです。

template<typename T, bool B>
struct SpecializedImpl;

template<typename T>
struct SpecializedImpl<T, true> {
  static void call() { 
    // ...
  }
};

template<typename T>
struct SpecializedImpl<T, false> {
  static void call() { 
    // ...
  }
};

template <typename T,bool B>
struct X
{
    void Specialized() { SpecializedImpl<T, B>::call(); }
};

通常はより多くのコードが必要であり、関数のオーバーロードの方が扱いやすいと思いますが、他の人はクラステンプレートの方法に従うことを好みます。結局は好みの問題です。この場合、その他のテンプレートをXネストされたテンプレートとして内部に配置することもできます-部分的にではなく明示的に特化する他のケースでは、明示的な特殊化を名前空間スコープでのみ配置できるため、それはできません。クラススコープに。

次のバリアントが最初のパラメーター変数も残すことを示すようにSpecializedImpl、関数のオーバーロードの目的のためだけにそのようなテンプレートを作成することもできます(その後、以前のように機能します)。i2t現在のインスタンス化のテンプレート パラメータ)

template <typename T,bool B>
struct X
{
private:
    // maps a type and non-type parameter to a struct type
    template<typename T, bool B>
    struct SpecializedImpl { };

public:
    void Specialized() { Specialized(SpecializedImpl<T, B>()); }

private:
    template<typename U>
    void Specialized(SpecializedImpl<U, true>) {
      // ...
    }

    template<typename U>
    void Specialized(SpecializedImpl<U, false>) {
      // ...
    }
};

別のテンプレートを延期する方が良い場合もあると思います(配列やポインターなどの場合、オーバーロードはトリッキーになる可能性があり、クラステンプレートに転送するだけの方が簡単でした)、テンプレート内でオーバーロードする方が良い場合もあります-特に、関数の引数を実際に転送する場合や、クラスのメンバー変数に触れる場合。

于 2009-10-01T01:19:20.133 に答える
3

これは私が思いついたもので、それほど悪くはありません:)

//The generic template is by default 'flag == false'
template <class Type, bool flag>
struct something
{
    void doSomething()
    {
        std::cout << "something. flag == false";
    }
};

template <class Type>
struct something<Type, true> : public something<Type, false>
{
    void doSomething() // override original dosomething!
    {
        std::cout << "something. flag == true";
    }
};

int main()
{
    something<int, false> falseSomething;
    something<int, true> trueSomething;

    falseSomething.doSomething();
    trueSomething.doSomething();
}
于 2009-10-01T00:57:09.557 に答える