4

それ自体がテンプレート クラスであるクラスに特殊化を提供しようとしているテンプレートを作成しています。それを使用するとき、私は実際にテンプレート化されたクラスの派生物でそれをインスタンス化しているので、次のようなものがあります:

template<typename T> struct Arg
{
    static inline const size_t Size(const T* arg) { return sizeof(T); }
    static inline const T*     Ptr (const T* arg) { return arg; }
};

template<typename T> struct Arg<Wrap<T> >
{
   static inline const size_t Size(const Wrap<T>* arg) { return sizeof(T); }
   static inline const T*     Ptr (const Wrap<T>* arg) { return arg.Raw(); }
};

class IntArg: public Wrap<int>
{
    //some code
}

class FloatArg: public Wrap<float>
{
    //some code
}
template<typename T>
void UseArg(T argument)
{
    SetValues(Arg<T>::Size(argument), Arg<T>::Ptr(&argument));
}

UseArg(5);
UseArg(IntArg());
UseArg(FloatArg());

いずれの場合も、最初のバージョンが呼び出されます。したがって、基本的に私の質問は次のとおりです。どこで間違ったのか、UseArg(5) を呼び出すときに arg を返すバージョンと、UseArg(intArg) を呼び出すときに別のバージョンを呼び出すにはどうすればよいですか? このようなことを行う他の方法 (UseArg のインターフェイスを変更せずに) はもちろん大歓迎です。

注意として、この例は少し単純化されており、実際のコードではより複雑なものをラップしており、派生クラスにはいくつかの実際の操作があることを意味しています。

4

4 に答える 4

4

次の3つのアプローチがあると思います。

1) Arg を派生型に特化します。

template <typename T> struct Arg ...
template <> struct Arg <IntArg> ...
template <> struct Arg <FloatArg> ...
// and so on ...

どんなタイプになるかを前もって知ることができないので、これは最悪です。もちろん、これらの型を取得したら特殊化できますが、これはこれらの型を実装する人が行う必要があります。

2) デフォルトのものを提供せず、基本的なタイプに特化する

template <typename T> struct Arg;
template <> struct Arg <int> ...
template <> struct Arg <float> ...
// and so on...
template <typename T> struct Arg <Wrap<T> > ...

それも理想的ではありません(使用する「基本タイプ」の数によって異なります)

3) IsDerivedFrom トリックを使用する

template<typename T> struct Wrap { typedef T type; };
class IntArg: public Wrap<int> {};
class FloatArg: public Wrap<float> {};

template<typename D>
class IsDerivedFromWrap
{
    class No { };
    class Yes { No no[3]; }; 

    template <typename T>
    static Yes Test( Wrap<T>* ); // not defined
    static No Test( ... ); // not defined 

public:
    enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) }; 
};


template<typename T, bool DerivedFromWrap> struct ArgDerived;

template<typename T> struct ArgDerived<T, false>
{
    static inline const T*   Ptr (const T* arg)
    {
        std::cout << "Arg::Ptr" << std::endl;
        return arg;
    }
};

template<typename T> struct ArgDerived<T, true>
{
    static inline const typename T::type* Ptr (const T* arg)
    {
        std::cout << "Arg<Wrap>::Ptr" << std::endl;
        return 0;
    }
};

template<typename T> struct Arg : public ArgDerived<T, IsDerivedFromWrap<T>::Is> {};

template<typename T>
void UseArg(T argument)
{
    Arg<T>::Ptr(&argument);
};

void test()
{
    UseArg(5);
    UseArg(IntArg());
    UseArg(FloatArg());
}

test() 出力を呼び出す (それがあなたの目標であることは理解しています):

Arg::Size 
Arg<Wrap>::Size
Arg<Wrap>::Size

Wrap のようなより多くの型で動作するように拡張することは可能ですが、厄介なこともありますが、うまく機能します。多くの特殊化を行う必要はありません。

言及する価値のあることの 1 つは、私のコードではの代わりに にArgDerived特化して いるため、 inを呼び出すと の代わりに のサイズが返されますが、それが意図されている場合は に変更できます。IntArgWrap<int>sizeof(T)ArgDerived<T, true>IntArgWrap<int>sizeof(Wrap<T::type>)

于 2010-01-05T10:58:20.833 に答える
-1

typedef Wrap<int> IntArg;導出する代わりに、問題を解決します。

実際、コンパイラは基本クラスの特殊化を検索しません。簡単な例を考えてみましょう:

class A { };
class B: public A { };
template<class T>
void f(T const &)
{ std::cout << "T" << std::endl; }
template<>
void f<A>(A const&)
{ std::cout << "A" << std::endl; }

int main()
{ f(A()); f(B()); }

「A T」とプリントされています。

于 2010-01-03T23:43:55.927 に答える