C++模板元编程 - 挖新坑的时候探索到了模板元编程的新玩法

C++真是一门自由的语言,虽然糖没有C#那么多,但是你想要怎么写,想要实现什么,想要用某种编程范式或者语言特性,它都会提供。

开大数运算类的新坑的时候(又是坑),无意中需要解决一个需求:大数类需要分别实现接受整数和浮点数的构造函数,构造函数中初始化类内保存数据的容器,所有整数的构造方法相同,所有浮点数的构造方法也相同,现在的目标是,找到一个机制,能尽可能少的实现代码,争取做到所有整数实现一份构造函数,所有浮点数实现一份构造函数。

学过C++的你一定会想到模板,这里有了

template<typename T>
Number(T num)
    : _num(ToVectorBool(num))
{}

这里我用了vector<bool>来储存一个大数的二进制位模式,ToVectorBool是将数字转换为vector<bool>

但是现在的模板还没有Concept,如何来限制ToVectorBool接受的模板参数只能是整数和浮点数呢?你可能想到用模板的特例化,各种浮点数的实现成为特例,而剩下的整数使用非特例的实现,然后用static_assert来限定非特例实现中不是整数类型的T。

然而不要忘了,特例化的函数是要每个特例写一份代码的,这样多不优雅啊。同样是利用函数重载决议,我们需要一个筛选器,判断类型参数T是整数还是浮点数还是什么都不是。

这样我们就回到了模板元编程的范围,我们需要写一个MultiState模板结构,它接受一个T,和用来接受这个类型T并返回结果的模板结构,用我在模板元编程代码中的约定,就是接受TStatement和TPreds...,依次判断TStatement是否符合TPreds...里每个谓词,返回判断的结果:在第几个谓词哪里得到了TrueType。

我们还需要typedef出State<N>,正好我以前写过Int<N>模板整数类型,就用它了

本着用户不乱用的原则,MultiState本身不能把参数N暴露给用户,于是套了一层MultiStateImpl,用MultiState去“调用”MultiStateImpl

为了可扩展性,这里使用了变长模板参数包,做了一个template<class T> class... TPreds,这样可以用模板参数包的一些技巧,做出接受任意个谓词的MultiState

template<int N>
using State = Int<N>;

template<int N, class TStatement, template<class T> class TPred, template<class T> class... TPreds>
struct MultiStateImpl
{
    using Result = typename If<
        typename TPred<TStatement>::Result,
        State<N>,
        typename MultiStateImpl<
            N + 1,
            TStatement,
            TPreds...
        >::Result
    >::Result;
};

template<int N, class TStatement>
struct MultiStateImpl
{
    using Result = FalseType;
};

template<class TStatement, template<class T> class... TPreds>
struct MultiState
{
    using Result = typename MultiStateImpl<0, TStatement, TPreds...>::Result;
};

可以看到,代码里首先检查TPred<TStatement>::Result,如果正确,直接返回State<N>,否则,返回下一层MultiState<N+1,TStatement, TPreds...>的结果,如果都不成立,到了MultiState<N, TStatement>的实现,就直接返回FalseType,表示哪个都不满足。

爽啊,用起来就是这样的

template<typename TNumber>
Number::Number(TNumber& number)
    : _num(ToVectorBool(number, MultiState<TNumber, IsInteger, IsFloat>::Result()))
{
}

template<typename TInteger>
static std::vector<bool> Number::ToVectorBool(TInteger& intNumber, State<1>)
{
    //Integer 实现略
}

template<typename TFloat>
static std::vector<bool> Number::ToVectorBool(TFloat& floatNumber, State<2>)
{
    //Float 实现略
}

template<typename TOther>
static std::vector<bool> Number::ToVectorBool(TOther& other, FalseType)
{
    static_assert(0, "Not a number.");
}

还可以改,把State也做成模板参数包,State<1>这样的写法还不优雅

感觉怪怪的,不过好有意思

posted on 2015-11-13 22:05  PointerSMQ  阅读(339)  评论(0编辑  收藏  举报