通过一个实例方法了解 C++ 的可变模版参数

对于实现基础框架的人来说,C++ 的可变模版参数真是太有用了,能够瞬间解决很多棘手的问题。下面这个例子就能够充分说明其各种强大的使用方式。

实例背景

先说一下这个例子的背景。

我们有一些配置参数,组合在一起形成若干的配置类。

为了统一处理,定义的配置项目抽象,每个配置项目与一个配置参数对应。上层只能通过配置项目实例来读、写对应参数的值。

一个类里面的参数存在一些关联性,在改变某个参数值的时候,有可能与其他参数冲突,所以需要校验,执行校验的方法称为一致性校验器。

我们为配置类添加多个(种)一致性校验器,每个校验器面向的一组有关联性的参数。

接口定义

template<typename T>
class ConfigItem : ConfigItemBase
{
};

class ConfigBase
{
};

class ConfigClass
{
public:
    ConfigClass();

    virtual void * addrOf(ConfigBase & cfg, ConfigItemBase const &) const;

    template<typename F, typename ...I>
    void addValidator(F validator, ConfigItem<I> const & ... items);

    typedef std::function<void (ConfigBase & cfg)> validator_t;

private:
    struct Validator
    {
        std::vector<ConfigItemBase const *> items;
        validator_t validator;
    };
    std::vector<Validator> validators_;
};

如上代码:

  • ConfigItem:配置项,是个模版类,继承 ConfigItemBase
    • 模版参数 T 表示对应参数的类型
  • ConfigBase:配置类的基础类
    • addrOf 返回其某个参数的地址
  • ConfigClass:配置类的元类,即所有操作配置类的方法,都是由元类提供
    • 内部一组一致性校验器
    • 方法 addValidator 就是我们需要通过可变模版参数实现的方法

代码实现

template<typename F, typename ...I>
inline void ConfigClass::addValidator(F validator, ConfigItem<I> const & ...items)
{
    validators_.push_back({&items...}, [this, validator, &items...](ConfigBase & cfg) {
        validator(*reinterpret_cast<I*>(addrOf(cfg, items))...);
    });
}

看看这段代码哪些地方用了可变模版参数,对了,就是所有三个点(...)的地方,我们来分析每一处对可变模版参数的使用方式。

  • typename ...I

这是最开始声明可变模版参数的地方,也就是我们的模版方法可以有不定数量的模版参数。

  • ConfigItem<I> const & ... items

这里有两点。

首先将可变模版参数 I 作为另一个模版类 ConfigItem<T> 的模版参数。你看直接用就行了,一切都是那么自然。

其次是作为方法的输入参数的类型,这里方法输入了不定数量的参数 items,并且参数的类型是对可变模版参数做了一定变化的 —— 除了变成另一个 ConfigItem<T> 模版类,还加上了 const & 限定符。注意三个点(...)放在实际声明符号(这里就是参数名称)的前面。

  • {&items...}

这里其实是要生成一个 std::vector<ConfigItemBase const *> 数组,使用初始化列表来构造数组。可以看出,可变模版参数修饰的变量可以用来构建一个初始化列表。

另外这里的取地址运算符 &,作用于所有不定数量的变量。

  • &items...

这里把可变模版参数修饰的变量,作为 lambda 表达式的环境参数传给 lambda 方法。

另外这里的取地址运算符 &,实际上表示使用引用的方式将变量传给 lambda 方法,并不是取地址。

  • reinterpret_cast<I*>

可以看到,这里可变模版参数被用来作为类型转换运算符的类型参数。

  • cfg.addrOf(items)...

这里把可变模版参数修饰的变量,分别作为另一个普通方法的参数,然后将返回值组合为另外一组不定数量的值,作为参数传给另一个方法(先经过若干运算符转换)。该方法的参数数目与可变模版参数的数量一样,类型有相关性。

使用接口

最后我们来看看如何使用 addValidator 方法给参数元类添加一致性校验器。

static ConfigItem<bool> item_bool;
static ConfigItem<int> item_int;

ConfigClass::ConfigClass()
{
    addValidator([](bool & b, int & i) {
        // DO validate
    }, item_bool, item_int);
}

上面的 lambda 表达式就是一个一致性校验器,它处理了两个参数的关联性,一个 bool 类型(item_bool)参数,一个 int 类型 (item_int)参数。

在一致性校验器内部,框架已经提供了这两个参数的当前值,校验器可以判断这两个值是否冲突,然后直接修改参数的值,是不是方便得不能再方便了?

可这么强大的机制,用可变模版参数实现起来就是小菜一碟,回过头再看看 addValidator 的实现吧,只有简简单单的 7 行!

还有好多 C++ 形式可以用可变模版参数扩展,你也可以继续试一试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting Horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值