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

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

实例背景

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

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

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

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

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

接口定义

template
class ConfigItem : ConfigItemBase
{
};

class ConfigBase
{
};

class ConfigClass
{
public:
    ConfigClass();

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

    template
    void addValidator(F validator, ConfigItem const & ... items);

    typedef std::function validator_t;

private:
    struct Validator
    {
        std::vector items;
        validator_t validator;
    };
    std::vector validators_;
};

如上代码:

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

代码实现

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

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

  • typename ...I

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

  • ConfigItem const & ... items

这里有两点。

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

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

  • {&items...}

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

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

  • &items...

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

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

  • reinterpret_cast

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

  • cfg.addrOf(items)...

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

使用接口

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

static ConfigItem item_bool;
static ConfigItem 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++ 形式可以用可变模版参数扩展,你也可以继续试一试。

你可能感兴趣的:(C/C++,c++,开发语言)