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

我认为有三种方法:

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 之类的更多类型,但也很麻烦,但它可以解决问题 - 你不需要做一堆专业化。

值得一提的是,在我的代码ArgDerived中专门使用了 而IntArg不是Wrap<int>,因此调用返回大小而不是,但如果这是您的意图,您可以将其更改为。sizeof(T)ArgDerived<T, true>IntArgWrap<int>sizeof(Wrap<T::type>)

于 2010-01-05T10:58:20.833 回答
0

正如 Nicht 所指出的,编译器不会搜索任何基类的特化。

与其专门化 Arg 类,不如重载UseArg 函数?有一个采用 T 的模板版本,并有一个采用 Wrap 的重载版本。重载版本可以根据需要“解包”到原始指针。

于 2010-01-04T01:37:20.753 回答
0

除了创建作为模板的 Arg 之外,您可以将其设为常规类,然后 OVERLOAD Size 和 Ptr 静态成员:

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

编辑:嗯,它不会工作,我刚刚检查过......

于 2010-01-04T12:04:07.743 回答
-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 回答