这个问题有点难解释,所以我先举个例子:
我有一个将类型和整数常量作为模板参数的类模板,并且我有许多从该模板的实例派生的子类:
template <class V, int i>
struct Base
{
static void doSomething() { cout << "something " << i << endl; };
};
struct Child : public Base<int,12>
{
};
我想将这些类与其他一些模板(我们称之为测试)一起使用,它具有针对不同类型的特化。因为从 Base 的任何实例派生的所有类的行为应该完全相同,所以我只想定义一个 Test 特化来处理从 Base 派生的所有类。
我知道我不能直接专门针对 Base<V,i> 因为这不会检测到子类。相反,我的第一种方法是使用 Boost 的 enable_if 和类型特征:
// empty body to trigger compiler error for unsupported types
template <class T, class Enabled = void>
struct Test { };
// specialization for ints,
// in my actual code, I have many more specializations here
template <class Enabled>
struct Test <int, Enabled>
{
static void test (int dst)
{
cout << "Test<int>::test(" << dst << ")" << endl;
}
};
// this should handle all subclasses of Base,
// but it doesn't compile
template <class T, class V, int i>
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type>
{
static void test (const T &dst)
{
dst.doSomething();
}
};
int main (int argc, char **argv)
{
Test <int>::test (23);
Test <Child>::test (Child());
return 0;
}
这个想法是,专业化应该处理所有从 Base 派生的具有任意 V 和 i 值的类。这不起作用,gcc抱怨:
错误:部分特化中未使用的模板参数: 错误:'V' 错误:“我”
我想问题是这种方法需要编译器尝试所有可能的 V 和 i 组合来检查它们是否匹配。现在,我通过在基类中添加一些东西来解决这个问题:
template <class V, int i>
struct Base
{
typedef V VV;
static constexpr int ii = i;
static void doSomething() { cout << "something " << i << endl; };
};
这样,特化不再需要有 V 和 i 作为自由模板参数:
template <class T>
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type>
{
static void test (const T &dst)
{
dst.doSomething();
}
};
然后它编译。
现在,我的问题是:如何在不修改基类的情况下做到这一点?在这种情况下,这是可能的,因为我自己编写了它,但是如果我必须像这样在我的测试模板中处理第三方库代码,我该怎么办?有没有更优雅的解决方案?
编辑:另外,有人可以给我一个详细的解释,为什么第一种方法不起作用?我有一个粗略的想法,但我希望有一个正确的理解。:-)