[C++] 从一个简单的 compare 说开去

By | 2015-05-01

compare 什么的大家都很熟了:

int compare(int, int) {...}

想写通用一些? 加强版:

template<typename T>
int compare(T const &, T const &) {...}

类型泛化? 默认类型参数? … 最终会出现类似这样的版本: (涉及到函数到底要重载还是特化, 以及函数模板不支持默认参数等问题, 此处暂不讨论该问题)

template<typename T0, typename T1>
class CompareHolder
{
public:
    static int compare(T0 const &, T1 const &) {...}
};
template<typename T0, typename T1>
int compare(T0 const &v0, T1 const &v1)
{
    return CompareHolder<T0, T1>::compare(v0, v1);
}

好了, 学院派的东西扯完了, 开始实用派, 首先自然是自定义类型的模板特化:

class YourType {...}
template<>
class CompareHolder<YourType, YourType>
{
public:
    static int compare(YourType const &, YourType const &) {...}
};

于是扯出了本文的重点: 当 YourType 有一个子类, 并且你希望不写任何代码, 让这个子类使用父类的特化版本, 应该如何做呢?

咋看一下很简单对吧? 写一写就知道个中蛋疼之处了, 有兴趣请先自行尝试体会一下


直接上最终的解决方案:

首先需要一个东西, 能够在编译期判断某个类是否是另一个类的子类

template<typename TChild, typename TBase>
class ClassIsTypeOf
{
private:
    class _Yes {};
    class _No {char dummy[16];};
    static _Yes _test(TBase *);
    static _No _test(...);
public:
    enum {value = (sizeof(_test((TChild *)0)) == sizeof(_Yes))};
};
#define CLASS_IS_TYPE_OF(TChild, TBase) ClassIsTypeOf<TChild, TBase>::value

如果 TChild 是 TBase 的子类, 则 _test 会调用到 "_Yes _test(TBase *)" 的版本, 最终导致 value 为 1, 而这些都是可以在编译期决定


然后需要一个东西, 能够把 0 和 1 变成 "可推导" 以及 "不可推导" 的东西 (STL 中有类似的东西: enable_if)

template<typename T, int condition>
class EnableIf
{
};
template
class EnableIf<T, 1>
{
public:
    typedef void Type;
};

有了这货, 由于 EnableIf<DummyType, 0>::Type 没有定义, 如果引用的话, 编译不通过, 可以造成依赖它的东西无法正常推导


接下来, 我们要改造一下 compare, 让它多几个空参数, 使得上面的 EnableIf 能排上用场

template<typename T0, typename T1, typename Fix0 = void, typename Fix1 = void>
class CompareHolder
{
public:
    static int compare(T0 const &, T1 const &) {...}
};

还有用于加载它的 loader:

template<typename T0, typename T1>
int compare(T0 const &v0, T1 const &v1)
{
    return CompareHolder<T0, T1>::compare(v0, v1);
}

最后, 把浑身解数都屎出来, 开始我们又臭又长的特化版本: (设名字叫 YourType0 和 YourType1)

template<typename T0, typename T1>
class CompareHolder<T0, T1
        , typename EnableIf<YourType0, CLASS_IS_TYPE_OF(T0, YourType0)>::Type
        , typename EnableIf<YourType1, CLASS_IS_TYPE_OF(T1, YourType1)>::Type
    >
{
public:
    static int compare(T0 const &, T1 const &) {...}
};

晕了没? 晕了就对了

这里特化的不是 T0, T1, 而是 Fix0, Fix1

当 T0/T1 不是 YourType0/YourType1 的子类时, EnableIf<YourType0, CLASS_IS_TYPE_OF(T0, YourType0)>::Type 最终的推导结果是未定义, 导致特化版本推导失败, 编译器就会自动使用非特化版本

反之, 虽然 EnableIf 最终的推导结果与非特化版本同样是 void, 但是由于其整体是作为一个 typename, 编译器会将其视为一个 type, 从而它会被视为一个推导成功的特化版本, 最终调用该特化的版本


STL 中应该有类似的处理方式, 至少有 std::enable_if, 具体的没细看, STL 什么的我就只用用 vector/map 连 iostream 都不用你以为我会乱说?

另外, 其实可以提供一个全局的 operator <, >, == 等比较函数, 但是某些情况下还是不方便, 而通过模板特化可以减小对原有代码的影响

C++ 果然很难啊… 以后再也不敢说精通 C++ 了

over

转载请注明来自: http://zsaber.com/blog/p/16

既然都来了, 有啥想法顺便留个言呗? (无奈小广告太多, 需审核, 见谅)

Category: C++

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注