我正在编写一个模板,我正在尝试为类本身提供专门化,而这个类本身就是一个模板类。在使用它时,我实际上是使用模板化类的派生来实现它,所以我有这样的东西:
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的界面)当然是受欢迎的。
作为一个注释,这个例子有点简单,意思是在实际的代码中我包装了一些更复杂的东西,派生类有一些实际的操作。
答案 0 :(得分: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>
会返回IntArg
而不是Wrap<int>
的大小,但您可以将其更改为{{ 1}}如果这是你的意图。
答案 1 :(得分:0)
正如Nicht所指出的,编译器不会搜索任何基类的特化。
为什么不重载 UseArg函数而不是专门化Arg类?有一个带有T的模板版本,并且有一个带有Wrap的重载版本。重载版本可以根据需要“解包”到原始指针。
答案 2 :(得分: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(); }
};
编辑: 好吧,它不会起作用,我刚刚检查过......
答案 3 :(得分:-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”。