模板基类的子类的模板特化

时间:2012-05-15 13:08:27

标签: c++ generics inheritance c++11 template-specialization

这个问题有点难以解释,所以我将从一个例子开始:

我有一个类模板,它将一个类型和一个整数常量作为模板参数,并且我有许多子类,这些子类派生自该模板的实例化:

template <class V, int i>
struct Base 
{
    static void doSomething() { cout << "something " << i << endl; };
};

struct Child : public Base<int,12>
{
};

我想将这些类与其他模板一起使用(我们称之为Test),它具有不同类型的特化。因为对于从Base的任何实例化派生的所有类,行为应该完全相同,所以我想只定义一个处理从Base派生的所有类的Test专门化。

我知道我不能直接专攻Base&lt; V,i&gt;因为这不会检测子类。相反,我的第一种方法是使用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抱怨道:

error: template parameters not used in partial specialization:
error:         ‘V’
error:         ‘i’

我想问题是这种方法需要编译器尝试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();
    }
};

然后它编译。

现在,我的问题是:如何在不修改基类的情况下执行此操作?在这种情况下,这是可能的,因为我自己写了,但如果我必须处理第三,我该怎么办?我的测试模板中的派对库代码是那样的吗?有更优雅的解决方案吗?

编辑:此外,有人可以给我详细解释为什么第一种方法不起作用?我有一个粗略的想法,但我更愿意有一个正确的理解。 : - )

2 个答案:

答案 0 :(得分:3)

一个简单的解决方案是让Base继承另一个Base_base

struct Base_base
{};

template <class V, int i>
struct Base
 :  public Base_base
{
    static void doSomething() { cout << "something " << i << endl; };
};

template <class T>
struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type>
{
    static void test (const T &dst)
    {
        dst.doSomething();
    }
};
在第三方代码中

[已编辑],您可以使用以下技巧:

template <class V, int i>
struct Base3rdparty
{
    static void doSomething() { cout << "something " << i << endl; };
};

template <class V, int i>
struct Base
 :  public Base3rdparty<V, i>
{
    typedef V VV;
    static constexpr int ii = 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();
    }
};

答案 1 :(得分:0)

使用函数重载和decltype

// Never defined:
template<typename T> std::false_type is_Base(T&);
template<class V, int I> std::true_type is_Base(Base<V,I>&);

template<typename IsBase, typename T> struct TestHelper;
template<typename T> struct TestHelper<std::true_type, T>
{
    static void test(const T& dst) { dst.doSomething(); }
};
template<> struct TestHelper<std::false_type, int>
{
    static void test(int dst)
    { std::cout << "Test<int>::test(" << dst << ")" << std::endl; }
};
// ...

template<typename T> struct Test
{
    static void test(const T& dst)
    { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); }
}