65.9K
CodeProject 正在变化。 阅读更多。
Home

静态值列表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (7投票s)

2011年8月25日

CPOL

18分钟阅读

viewsIcon

25088

downloadIcon

260

通过解释性项目介绍高级模板元编程。

目录

  1. 引言
  2. Using the Code
  3. 代码演练
    1. 静态值列表 - 基础实现
    2. 通过可变模板(仅限C++0x)构建列表
    3. 为列表项索引检索值范围
    4. 为值检索值索引
    5. 在给定项索引处插入新项
    6. 检查静态列表是否升序
    7. 排序列表
    8. 压缩列表
    9. 运行时功能
    10. 代码示例
  4. 替代方案
  5. 关注点
  6. 参考文献
  7. 历史

1 引言

有很多方法可以让你在运行时构建各种列表。这是每个人在工作中每天都需要和使用的。例如:

int my_array[5];
std::vector<int> my_vector;
CString s; // Can be seen as a list of characters

幸运的是,本文不是关于这些的,因为它们很无聊。它关于在编译时构建和查询的值列表——因此得名“静态值列表”。这里的“静态”一词指的是“在编译时固定”,就像在静态断言中一样。

使用现有的工具无法创建这样的结构,即使有以下定义:

const static int arr[] = {1, 2, 3};

当然,arr的内容在运行时无法修改,并且可以视为在编译时固定。但它们无法在编译时查询(例如,无法编写类似my_class_template<arr[2]>的内容)。

如果您还不熟悉这个概念,您可能会问“我需要这个做什么?”

很有可能您不需要。但是,有一些事情可以用静态值列表来完成:

  • 在编译时计算斐波那契数并将它们存储在列表中:与42不同,这里的问题是已知的,而且我不知道有谁不喜欢拥有尽可能多的算法来估算不朽兔子的繁殖。
  • 在我题为“枚举列表和枚举数组”的文章中,我使用了这个概念(不是相同的代码,但我打算将来更新它)来构建枚举列表。通过这样做,提高了值的安全性。
  • 通过这里使用的众所周知的方法,为模板元编程(TMP)提供一个介绍。

2 使用代码

本文附带的源代码结构如下:

  • lobster:一个仅限头文件的库,提供一个公共命名空间。文件夹内包含可以外部引用的头文件。例如,包含“static_list.h”将加载处理静态列表所需的所有文件。因此,在示例代码中,此文件夹被命名为附加包含文件夹。
    • static_list:此处解释了提供的模板命名空间。
    • ...:此处未讨论的其他命名空间。
  • static_list_sample
    • source:包含用于比较示例的代码文件。
      • enm_demo.h/.cpp:在C++中构建枚举列表(一个静态值列表,索引为枚举数)的示例。
      • enm_demo_0x.h/.cpp:在C++0x中构建枚举列表的示例。展示了enum class和可变类模板的使用。仅当存在LOBSTER_CPP0X定义时才编译。请注意,VS2008不提供所需的功能(VS2010可能可以),因此我仅使用GCC进行了测试。
      • int_demo.h/.cpp:构建静态整数列表并对其执行转换的示例。
      • fib_demo.h/.cpp:计算斐波那契数并将其存储在静态值列表中的示例。
    • gcc:使用gcc编译示例的批处理文件。输出将是同一文件夹中的可执行文件。
    • msvc:VS2010的工作区和项目文件。

执行代码示例时,将显示上述静态值列表的演示。源代码包含BAD_IDEA_X定义,可以启用这些定义来测试将(或不应)编译的代码行。

在代码详解之后,示例将更加清晰。

3 代码详解

根本思想是创建最小的基础模板来定义静态列表,并在附加模板中提供附加功能(请参阅KISS原则)。在该代码的早期版本中,我尝试将功能捆绑在一起,但这变得太复杂了,因为我担心嵌入在其他类模板中的类模板以及决定谁应该成为谁的朋友(而且模板友元声明会很长)。

3.1 静态值列表 - 基础实现

创建元数据(Abrahams2005)列表的唯一选择是通过递归来实现,因为C++ TMP本质上是纯粹功能的。基本上,所有列表项都必须堆叠在一起。在列表的末尾,需要某种列表尾部。这里的“堆叠”一词可以在最真实的意义上理解,它上面的每个操作都必须从顶部开始。

每个列表项必须提供以下信息:

  • 对其父列表的引用(它在其上构造的堆栈元素)。在类型列表出版物中,这也被称为尾部(Alexandrescu2001),与头部相对,后者是当前正在查看的列表元素。请注意,这与这里使用的“尾部”一词的用法不同。
  • 此列表项内容的值。在此解决方案中,我实际上使用了两个值:value_firstvalue_last。这允许在每个列表项中存储值的范围——并且也允许列表项中只有一个值作为特殊情况。

仍然,我们不完整——例如,静态列表的值类型仍然缺失。由于我不想为每个列表项定义此信息,因此它存储在列表尾部:

  • 存储值的value_type对于所有列表项都是相同的。
  • 呈现了comparable_type类型,该类型可用作比较列表项值的转换目标。某些值类型无法直接比较(例如,C++0x enum class),因此有必要将其转换为允许比较运算符的内容。此类型默认为int,这可能足以满足实际用途。

下图提供了光学表示:

static value list example

从TMP的角度来看,类模板承担类的作用,而实例化的类模板则承担对象的作用。图中所示的实体是实例化的类模板,但被呈现为对象,因为它们在这里的作用是如此。

让我们看看列表项的代码:

template<
    typename _parent_type,
    typename _parent_type::value_type _value_first,
    typename _parent_type::value_type _value_last = _value_first
    >
class list_item
{
public:
    typedef _parent_type parent_type;
    typedef typename _parent_type::value_type value_type;
    typedef typename _parent_type::comparable_type comparable_type;

    static const value_type value_first = _value_first;
    static const value_type value_last  = _value_last;

    static const int size = parent_type::size + 1;

    static const int value_count =
        parent_type::value_count
        + (comparable_type) (value_last)
        - (comparable_type) (value_first)
        + (comparable_type) (1);

    inline static int index_of(value_type e) {
        ...
    }

    inline static value_type at(int index) {
        ...
    }

    ...

private:
    // Instantiation is not allowed
    list_item() {};
};

您可能已经注意到,我实际上已经违反了代码详解开头提到的KISS原则。除了强制部分外,这个类模板还包含运行时使用的方法(另一方面也相当方便),以及sizevalue_countvalue_typecomparable_type等结构。后者实际上可以毫无缺点地外部化。

size值存储了当此列表项被视为头部时列表中列表项的数量。value_count包含值的总数。因此,列表有两个索引:

  • 列表项索引提供对表示值范围的list_item的访问。
  • 值索引提供对特定值的访问。

下表演示了区别:

list_item<list_item<list_item<list_tail<int>, 1>, 3, 7>, -11> my_complex_list;
列表项 列表项 列表项
项索引 0 1 2
值索引 0 1, 2, 3, 4, 5 6

1 3, 4, 5, 6, 7 -11

尾部更简单,定义在“sl_list_tail.h”中。

template<typename _value_type, typename _comparable_type = int>
class list_tail {
public:
    typedef _value_type value_type;
    typedef _comparable_type comparable_type;

    static const int size = 0;
    static const int value_count = 0;

    inline static int index_of(value_type) {
        ...
    }

    inline static value_type at(int) {
        ...
    }
};

静态列表是通过将列表项嵌入彼此中,以列表尾部作为底部元素来构建的。

typedef list_item<list_item<list_item<list_tail<int>, 1>, 2>, 3> my_list;

键入此内容很麻烦,“sl_shortcut.h”和“sl_shortcut_0x.h”(仅限C++0x)提供了typedefs以简化类型创建。使用它们,可以通过以下方式更轻松地创建相同的类型:

typedef list_3<list_tail<int>, 1, 2, 3>::type my_second_list; // for C++,
// class templates list_1 to list_10 are provided. The list size has
// to be predefined as class templates cannot overload.

typedef var_list<list_tail<int>, 1, 2, 3>::type my_third_list; // for C++0x,
// a variadic class template is used

下一个部分将讨论可变模板。您可能想知道为什么我让用户在类型定义中键入list_tail。这是为了允许通过进一步包装来扩展列表。

typedef list_1<my_list, 4>::type my_extended_list;

通过使用此方法,即使只定义了从list_1list_10的快捷方式,也可以构建包含超过十个值的静态值列表。

3.2 通过可变模板(仅限C++0x)构建列表

可变模板因其“...”语法而脱颖而出。如果您是第一次接触它们,Gregor2006 提供了一个介绍。

sl_shortcut_0x.h”的代码如下:

template<
    typename tail,
    typename tail::value_type... args
> struct var_list;

首先,模板仅被声明(通常,此时,可以已经定义了非特化的行为)。这是构造常规模板时的最佳实践,但对于可变模板来说,它是一个必须项,因为GCC 4.5.2无法解析可变模板代码。

下一步是2个及以上值的模板特化。这已经是特化了,因为可变模板参数被分解为头部“v”和尾部“args...”。

template<
    typename tail,
    typename tail::value_type v,
    typename tail::value_type... args
> struct var_list<
    tail,
    v,
    args...
>
{
    typedef typename var_list<list_item<tail, v>, args...>::type type;
};

还需要最后一个枚举器的部分特化(此时args...为空)来停止递归。

template<
    typename tail,
    typename tail::value_type v>
struct var_list<
    tail,
    v
> {
    typedef list_item<tail, v> type;
};

这种方法的一个亮点是,项的添加顺序与基本方法完全相同。这是通过此处的2+参数特化中类模板的排序来完成的。

typedef typename var_list<list_item<tail, v>, args...>::type type; 

如果这一行是

typedef typename list_item<var_list<tail, args...>, v>::type type; 

静态值列表将以相反的顺序创建。从可变模板参数中提取的第一个值将是最外层的,因此是结果中的最后一个。这实际上让我们得以一窥TMP中的算法。以类型形式给出的信息总是最终的,但可以基于其构建新类型。

但是,我们仍然无法处理静态值列表。因此,是时候通过提供附加模板来扩展功能了。这些功能通常由模板类提供,这些模板类以静态值列表为参数,并包含一个public typedefstatic const值,该值包含结果。

这些结构被称为元函数(Abrahams2005)。

3.3 为列表项索引检索值范围

在展示代码之前,我想先完成一个规划阶段。在开始键入之前,有必要从命令式思维转向函数式思维。这以类似于Alexandrescu2001,第3.6章及后续章节的方式完成。

命令式地,我们会简单地说:

Give me the n-th list item of the list!

用递归表达它并不容易:

Choose depending on the list_item<...>::size
    If the current item is the one I want,
        return its values
    Otherwise go to the parent list item, try again and return
        the values delivered from there

此外,我们需要清楚地定义输入和输出:

输入静态值列表项和该列表的索引。
输出:以list<...>::value_firstlist::<...>::value_last表示的值范围。

输入信息导致模板声明:

template<
    typename list,
    int index>
struct value_at;

然后使用输出信息来定义结果。这将是模板(特化)体中的static const list::value_type value_first = ...定义。

接下来,对于非平凡的编译时决策,可以使用具有不同特化的辅助类模板。可以在类模板中计算值(例如,使用可能在编译时使用的?运算符),但无法在模板内部切换编译路径。为了做到这一点,我们让编译器根据?运算符选择一个辅助模板特化:

template<typename list, int index>
struct value_at
{
    static_assert((0 <= index) && (index < list::size),
        "index not found in static list");

    typedef value_at_helper<list, index,  index == list::size - 1> helper;

    const static typename list::value_type value_first =
        helper::value_first;

    const static typename list::value_type value_last =
        helper::value_last;
};

可以看到,决策是通过比较index == list::size - 1来完成的。它决定调用辅助模板类的哪个特化。然后从该特化中查询结果。

辅助模板通常接受与主模板相同的参数,外加决策参数。对于二元决策,布尔值就足够了。(对于更复杂的决策,我更喜欢枚举。)因此,辅助模板具有以下声明:

template<
    typename list,
    int index,
    bool index_found
> struct value_at_helper;

声明之后是两个特化,每个情况一个:

template<
    typename parent,
    typename parent::value_type v1,
    typename parent::value_type v2,
    int index
> struct value_at_helper<
    list_item<parent, v1, v2>,
    index,
    false
> {
    const static typename parent::value_type value_first =
        value_at<parent, index>::value_first;

    const static typename parent::value_type value_last =
        value_at<parent, index>::value_last;
};

此特化用于将索引比较传播到父项。因此,它在特化定义中从给定的静态列表提取父类型,并为该父类型调用value_at<...>

template<typename list, int index>
struct value_at_helper<
    list,
    index,
    true
> {
    const static typename list::value_type value_first =
        list::value_first;

    const static typename list::value_type value_last =
        list::value_last;
};

此特化用于在找到搜索的索引时。它返回与list模板参数关联的值。随着递归的展开,结果被传播。

整个结构可以看作一个元函数。例如,它通过以下方式调用:

// For the first value in the range
const static int i1 = value_at<my_list, 3>::value_first;

// For the last value in the range
const static int i2 = value_at<my_list, 3>::value_last; 

可以看到,元函数可以有多个返回值。

主模板类和辅助模板类的组合是该库中某些函数中发现的模式。它不是唯一的选择,在许多情况下只需要一个具有不同特化的模板类。在源文件中,可以找到一个禁用的部分(LOBSTER_INDEX_OF_BAD_ALTERNATIVE),该部分尝试在一个基础模板中处理递归。如果您尝试使其工作,您会发现会遇到严重的障碍。

3.4 为值检索值索引

对于新操作,我们重复定义输入和输出的过程——然后定义函数算法:

输入:列表和一个值。
输出:给定值的*值索引*。

算法可以表述如下:

Choose depending of value range of current list item
    If the one containing the given value,
        return its value index
    Otherwise acquire its parent type and return the
        value index delivered by it

模板类声明与value_at的声明几乎相同:

template<
    typename this_type,
    typename this_type::value_type test_value
> struct value_index_of;

在实现中,辅助模板被一个底部插头替换,该插头在找不到值时返回-1

template<
    typename value_type,
    value_type test_value
>
struct value_index_of<
    list_tail<value_type>,
    test_value>
{
    enum {
        value = -1
    };
};

此特化在找到列表尾部时中止搜索并返回-1。此外,这里的返回类型也不同。虽然上面,我使用了static const限定符来创建编译时值,但这里使用仅包含一个值的枚举来实现相同的功能。从外部看,这两种方法看起来几乎相同。尽管枚举方法在示例中经常出现,而且也更简洁,但我倾向于使用限定符的方式。例如,对于value_at,需要正确的返回类型,否则用户将不得不将其转换回。

只要找不到正确的索引,搜索就会继续,通过递归调用此特化:

template<
    typename _parent_type,
    typename _parent_type::value_type value_first,
    typename _parent_type::value_type value_last,
    typename _parent_type::value_type value_test
>
struct value_index_of<
    list_item<_parent_type, value_first, value_last>,
    value_test>
{
    typedef typename _parent_type::comparable_type comparable_type;

    enum {
        value = ((comparable_type) (value_first) <= (comparable_type) (value_test))
             && ((comparable_type) (value_test)  <= (comparable_type) (value_last))
                ?
            _parent_type::size + (comparable_type) (value_test) -
				(comparable_type) (value_first)
                :
            index_of<_parent_type, value_test>::value
    };
};

现在将comparable_type投入使用,以便比较那些无法通过<=直接比较的类型。再次,特化推导了父列表项,也推导了此列表项的值范围。可以看到,返回值不是列表项索引,而是值索引。因此,列表的所有值索引都通过0 <= index_of<list, value>::type <= list::value_count进行索引。

同样可以看到,在这一部分(以及之前使用的部分)的上下文中,辅助模板是不必要的。决策和返回值计算可以在一步中完成,当元函数递归总是停止在固定的项索引(此处为0 - 通过到达尾部指示)时。

3.5 在给定项索引处插入新项

插入项是典型的运行时列表操作,我也希望它在我的静态列表中。该操作的完整命令式描述如下:

在给定的项索引处将一个项插入到静态列表中。如果索引为0,则新列表项将成为列表中的第一个项。如果列表中不存在该索引,则新项将被追加到列表末尾。

输入:原始列表、插入项索引以及新项应携带的值范围。
输出:更新后的列表(旧列表必须保留,因为编译时对象本质上是不可变的)。

输入和输出定义了以下主模板声明:

template<
    typename list_item,
    int idx,
    typename list_item::value_type new_value_first,
    typename list_item::value_type new_value_last = new_value_first
> struct list_insert_at;

翻译成函数式语言,代码将类似于:

Choose depending on the presence of the insertion position in the static list:
    If the insertion position is reached, create the new list
        item on top of it
    If the insertion position is not yet reached, traverse
       further into the list
    If the insertion position is not inside the list, simply
       append the new item at the end of the list

可以看到,2个决策选项在这里是不够的。因此,我将使用枚举来可视化不同的选项:

enum index_pos {
    index_equals,
    index_in_range,
    index_out_of_range
};

这将使分析位置的主模板稍微复杂一些,但辅助模板的方法与3.3中的方法相同。

template<
    typename list_item,
    typename list_item::value_type new_value_first,
    typename list_item::value_type new_value_last,
    int idx,
    index_pos index_tag
> struct list_insert_at_switch;

...

template<
    typename list,
    int idx,
    typename list::value_type new_value_first,
    typename list::value_type new_value_last
> struct list_insert_at
{
    const static index_pos ip =
        idx == list::size - 1 ? index_equals :
        idx >= list::size ? index_out_of_range :
        idx < 0 ? index_out_of_range :
        index_in_range;

    typedef typename list_insert_at_switch<
        list,
        new_value_first,
        new_value_last,
        idx,
        ip
    >::type type;
};

同样,辅助模板特化的实现保持不变:

#pragma region "list_insert_at_switch<..., index_in_range>"

template<
    typename parent_list,
    typename parent_list::value_type parent_value_first,
    typename parent_list::value_type parent_value_last,
    typename parent_list::value_type new_value_first,
    typename parent_list::value_type new_value_last,
    int idx
> struct list_insert_at_switch<
    list_item<parent_list, parent_value_first, parent_value_last>,
    new_value_first,
    new_value_last,
    idx,
    index_in_range
> {
    typedef list_item<
        typename list_insert_at<parent_list, idx, new_value_first, new_value_last>::type,
        parent_value_first,
        parent_value_last
    > type;
};

#pragma endregion

#pragma region "list_insert_at_switch<..., index_equals>"

template<
    typename parent_list,
    typename parent_list::value_type parent_value_first,
    typename parent_list::value_type parent_value_last,
    typename parent_list::value_type new_value_first,
    typename parent_list::value_type new_value_last,
    int idx
> struct list_insert_at_switch<
    list_item<parent_list, parent_value_first, parent_value_last>,
    new_value_first,
    new_value_last,
    idx,
    index_equals
> {
    typedef list_item<
        list_item<parent_list, new_value_first, new_value_last>,
        parent_value_first,
        parent_value_last
    > type;
};

#pragma endregion

#pragma region "list_insert_at_switch<..., index_out_of_range>"

template<
    typename list,
    typename list::value_type new_value_first,
    typename list::value_type new_value_last,
    int idx
> struct list_insert_at_switch<
    list,
    new_value_first,
    new_value_last,
    idx,
    index_out_of_range
> {
    typedef list_item<
        list,
        new_value_first,
        new_value_last
    > type;
};

#pragma endregion

3.6 检查静态列表是否升序

可以对列表执行许多算法,我在“sl_is_ascending.h”中选择此简单算法作为示例。

输入:要测试的列表。
输出:一个布尔值,如果列表升序则为true,否则为false

这导致以下声明:

template<typename list> struct is_ascending;

算法可以递归地编写为:

Choose depending on if the first list item is reached
    If it is,
        return true only if its last value is higher
        than its first one
    Otherwise return true only if the parent list
        is ascending, the last value in this item
        is higher equal than the first and the first
        value in this list higher than the last of
        the parent list item

这可以毫不费力地实现,而无需使用辅助类模板:

// Specialization for all list items that are not the first one
template<
    typename list,
    typename list::value_type parent_value_first,
    typename list::value_type parent_value_last,
    typename list::value_type value_first,
    typename list::value_type value_last
> struct is_ascending<
    list_item<
        list_item<
            list,
            parent_value_first,
            parent_value_last
        >,
        value_first,
        value_last
    >
> {
    typedef list_item<
        list,
        parent_value_first,
        parent_value_last
    > parent_type;

	typedef typename parent_type::comparable_type comparable_type;

    const static bool value =
        is_ascending<parent_type>::value
        && ((comparable_type) (value_first) > (comparable_type) (parent_value_last))
        && ((comparable_type) (value_last) >= (comparable_type) (value_first));
};

// Specialization for the first item in the list (which is not covered by the
// previous specialization)
template<
    typename value_type,
    value_type value_first,
    value_type value_last,
    typename comparable_type
> struct is_ascending<
    list_item<
        list_tail<
            value_type,
            comparable_type
        >,
        value_first,
        value_last
    >
> {
    const static bool value = ((comparable_type) (value_last) >=
				(comparable_type) (value_first));
};

在此代码示例中,模板特化头的重要性与复杂性非常清楚——它们不仅用于定义类型和值,还用于特化主体中访问它们的唯一方式。熟悉这里呈现信息的方式需要时间,甚至在这样的头文件中定位模板类名称也需要时间。

由于参数的数量、在类命名和特化定义之前必须声明它们,因此我决定将每个参数放在单独的代码行上,并使用缩进可视化模板包装级别。

3.7 排序列表

如果可能测试静态值列表是否升序,那么它也可以以这种方式排序。请参阅“sl_sort_ascending.h”中的代码,因为它比以前的代码示例简单得多,因为它使用了其他模板list_insert_at(参见3.5)和list_find_insert_pos(“sl_find_insert_pos.h”)。后者的工作方式与3.5类似。

此时,我将不深入研究实现——尽管它很复杂,但它不会为解释增加任何新内容。对于模板用户来说,排序列表就像编写以下代码一样简单(示例取自“enm_demo.cpp”):

typedef sort_ascending<weekdays>::type sorted_weekdays;

3.8 压缩列表

当使用list_Nvar_list模板构建静态列表时,每个列表项只包含一个值。这有利于安全性(因为每个列表项都必须明确命名,因此不允许其他值混入),但在运行时可能会导致性能下降,因为此时的值访问时间是N的量级。因此,能够压缩列表将是很好的——这是通过以下代码行完成的(示例取自“enm_demo.cpp”):

typedef compact<weekdays>::type compacted_weekdays;

这将创建一个新的静态列表,连接所有相邻的值,只要增量方向保持不变。

3.9 运行时功能

以下代码示例利用“sl_to_string.h”功能,将静态列表的内容输出到控制台。这主要是调试和演示功能。例如,在“enm_demo.cpp”中使用的以下行调用COUT_INFO宏,首先将to_string函数模板本身作为标题输出,然后输出其结果。

COUT_INFO(to_string<workdays>().c_str());

此调用将输出以下内容:

to_string<workdays>().c_str()
-----------------------------
List Item  Start        End
        0:         1 ->         1
        1:         2 ->         2
        2:         3 ->         3
        3:         4 ->         4
        4:         5 ->         5

另一个运行时功能是通过“sl_list_iterator.h”提供的与STL迭代器非常相似的功能来遍历静态列表的能力。在示例中,此功能完全未使用,甚至to_string也使用编译时递归来遍历列表的列表项。

如果使用运行时迭代,它将如下所示(compressed_weekdays在“enm_demo.cpp”中找到):

for (compressed_weekdays::const_iterator it = compressed_weekdays::begin();
    it != compressed_weekdays::end();
    it++)
{
    std::cout << *it << std::endl;
}

3.10 代码示例

enm_demo.h/.cpp

展示了使用list_N模板创建包含枚举数的静态列表,向该列表插入更多项,排序和压缩。

enm_demo_0x.h/.cpp

展示了从C++0x enum class枚举创建包含枚举数的静态列表。此外,还使用了可变模板var_list(参见3.2),而不是list_N列表。此示例只能使用GCC编译,因为VC++ 2010不支持可变模板。

int_demo.h/.cpp

使用int作为基本类型而不是枚举的演示。

fib_demo.h/.cpp

生成斐波那契数列表并将其存储在静态列表中。不要认为它比纯递归解决方案(不使用静态列表)在编译时间上有所好处。每个新值都由编译器存储在一个新类型中,该类型无论如何都只生成一次。

这显然与运行时斐波那契计算不同,后者避免预存储结果会导致N约等于40时计算时间几乎无限。

因此,使用静态列表缓存斐波那契临时结果没有任何好处。

4 替代方案

静态值列表不必从头开始设计。例如,静态值列表可以基于C++ TR1扩展、C++0x、Loki或Boost库提供的类型列表来实现。其思想是提供一个表示值的类型的类型列表。为此,可以使用int2type模板,如Alexandrescu2001中所述。

Boost元编程库(MPL)已经提供了基于Boost类型列表创建静态值列表的模板,其中该功能称为**整数序列包装器**。可以在Abrahams2005,第3章中找到介绍。第4章然后讨论整数类型包装器。

5 注意事项

我发现了一件事:在TMP方面,一些基本的编程规则比以往任何时候都更重要:

KISS(保持简单+自我侮辱):在进行模板编程时,请保持尽可能简单。切勿使用比必需更复杂的结构。在常规编程中,通常可以逃脱,但在那里,它将花费大量开发时间,并且可能没有任何收益。

分而治之:尽可能分离功能。如3.7中所述,使用子函数可以创建可单独测试的代码单元(即使它们仅在一个上下文中被使用)——如果您只有糟糕的调试选项,这是一个巨大的优势。

6 参考书目

  • David Abrahams, Aleksey Gurtovoy. 2005. C++ template metaprogramming: concepts, tools, and techniques from boost and beyond
  • Douglas Gregor, Jaakko Jarvi, Gary Powell. 2005. Variadic Templates (Revision 3) (Document n2080 on open-std.org)
  • Andrei Alexandrescu. 2001. Modern C++ Design: Generic Programming and Design Patterns Applied

7 历史

  • 2011/08/24 版本 1
© . All rights reserved.